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

时间:2010-08-30 12:19:41

标签: java collections duplicates equals

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

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

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

我打算在FormObject类上有一个名为removeDuplicateNewBooks的方法,它将执行上述功能。

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

3 个答案:

答案 0 :(得分:7)

您可以将TreeSet与自定义Comparator<Book>

一起使用
  • 构建TreeSetComparator实现您想要的自定义逻辑
  • 使用set.addAll(bookList)

现在Set只包含独特的图书。

答案 1 :(得分:4)

使新书独一无二:

围绕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() 
        };
    }

}

修改 优化了equals和hashCode并修复了hashCode中的错误。

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

Set<Wrapper> wrappers = new HashSet<Wrapper>();
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方法更好)

答案 2 :(得分:1)

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

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

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

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

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

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

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

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

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