比较两个列表并从中删除重复项

我有一个名为FormObject的对象,它包含两个ArrayLists – oldBooks和newBooks – 两者都包含Book对象。

oldBooks允许包含重复的Book对象newBooks不允许在其自身中包含重复的Book对象,并且不能在oldBooks列表中包含任何Book对象的重复项。

重复Book的定义很复杂,我无法覆盖equals方法,因为定义在Book对象的所有用途中都不是通用的。

我计划在FormObject类上有一个名为removeDuplicateNewBooks的方法,它将执行上述function。

你会如何实现这个? 我的第一个想法是使用HashSets消除重复但不能在Book对象上覆盖equals意味着它将无法工作。

您可以将TreeSet与自定义Comparator

  • 使用Comparator构造TreeSet ,实现所需的自定义逻辑
  • 使用set.addAll(bookList)

现在,该Set仅包含独特的书籍。

为了使新书独特:

在Book周围创建一个包装类,并根据附带的book对象声明它的equals / hashCode方法:

 public class Wrapper{ private final Book book; public Wrapper(final Book book){ assert book != null; this.book = book; } public Book getBook(){ return this.book; } @Override public boolean equals(final Object other){ return other instanceof Wrapper ? Arrays.equals( this.getBookInfo(), ((Wrapper) other).getBookInfo() ) : false; } @Override public int hashCode(){ return Arrays.hashCode(this.getBookInfo()); } private String[] getBookInfo(){ return new String[] { this.book.getAuthor(), this.book.getTitle(), this.book.getIsbn() }; } } 

编辑:优化等于和hashCode并修复hashCode中的错误。

现在使用一个集来删除重复项:

 Set wrappers = new HashSet(); for(Book book: newBooks){ wrappers.add(new Wrapper(book); } newBooks.clear(); for(Wrapper wrapper: wrappers){ newBooks.add(wrapper.getBook()); } 

(但是使用自定义比较器的TreeSet答案当然更优雅,因为你可以使用Book类本身)

编辑:(删除了对apache commons的引用,因为我改进的equals / hashCode方法更好)

HashingStrategy是您正在寻找的概念。 它是一个策略接口,允许您定义equals和hashcode的自定义实现。

 public interface HashingStrategy { int computeHashCode(E object); boolean equals(E object1, E object2); } 

Eclipse Collections包括哈希表以及基于散列策略的迭代模式。 首先,您将创建自己的HashingStrategy来回答两Books是否相同。

接下来,您将使用distinct()来删除newBooksUnifiedSetWithHashingStrategy中的重复项,以消除列表中的重复项。

 List oldBooks = ...; List newBooks = ...; HashingStrategy hashingStrategy = new HashingStrategy() { ... }; Set set = UnifiedSetWithHashingStrategy<>(hashingStrategy, oldBooks); List result = ListIterate.distinct(newBooks, hashingStrategy).reject(set::contains); 

distinct()方法根据散列策略仅返回唯一项。 它返回一个列表,而不是一个集合,保留原始订单。 根据相同的散列策略,对reject()的调用将返回另一个没有该集合包含的元素的新列表。

如果您可以更改newBooks以实现Eclipse Collections接口,则可以直接调用distinct()方法。

 MutableList newBooks = ...; MutableList result = newBooks.distinct(hashingStrategy).reject(oldBooks::contains); 

注意:我是Eclipse Collections的提交者。