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

Java 比较两个列表并从一个列表中删除重复项,java,collections,duplicates,equals,Java,Collections,Duplicates,Equals,我有一个名为FormObject的对象,它包含两个ArrayList——oldBooks和newBooks——它们都包含Book对象 允许旧图书包含重复的图书对象 newBooks本身不允许包含重复的图书对象,并且不能在oldBooks列表中包含任何图书对象的副本 重复图书的定义很复杂,我不能覆盖equals方法,因为该定义在图书对象的所有用途中都不是通用的 我计划在FormObject类上有一个名为RemovedUpplicateNewBooks的方法,它将执行上述功能 你将如何着手实施这一点

我有一个名为FormObject的对象,它包含两个ArrayList——oldBooks和newBooks——它们都包含Book对象

允许旧图书包含重复的图书对象 newBooks本身不允许包含重复的图书对象,并且不能在oldBooks列表中包含任何图书对象的副本

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

我计划在FormObject类上有一个名为RemovedUpplicateNewBooks的方法,它将执行上述功能

你将如何着手实施这一点?我的第一个想法是使用hashset来消除重复项,但不能覆盖Book对象上的equals意味着它将不起作用。

您可以将a与自定义的
比较器一起使用:

  • 使用实现所需自定义逻辑的
    比较器构建
    树集
  • 使用
    set.addAll(图书列表)

现在,
集合
只包含唯一的书籍。

要使新书具有唯一性:

围绕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中的一个bug

现在使用集合删除重复项:

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());
}
Set wrappers=new HashSet();
用于(书籍:新书){
添加(新的包装器(书);
}
newBooks.clear();
for(包装器:包装器){
add(wrapper.getBook());
}
(当然,带有自定义比较器的TreeSet答案更优雅,因为您可以使用Book类本身)

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

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

public interface HashingStrategy<E>
{
    int computeHashCode(E object);
    boolean equals(E object1, E object2);
}
distinct()
方法根据散列策略只返回唯一的项。它返回一个列表,而不是一个集合,保留原始顺序。调用
reject()
根据相同的散列策略返回另一个新列表,其中不包含集合包含的元素

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

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

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

那么,您如何检查图书是否相等?如果您知道确定重复的逻辑,那么您应该能够使用相同的逻辑覆盖equals方法。如果图书相等的定义不是通用的,您将如何通用地确定图书是否是重复的?这有什么不同将其重写等于,而不是以其他方式定义它?嗯,这是一个有趣的解决方案,绝对不是我会想到的。我考虑过这一点,但在语义上使用比较器是“错误的”,因为比较器应该用于对对象进行排序,而不是测试平等的替代定义。是的,但契约是正确的当且仅当对象相等时,比较器返回0,这意味着相等测试。是的,这是真的。这绝对是最优雅的解决方案。
MutableList<Book> newBooks = ...;
MutableList<Book> result = newBooks.distinct(hashingStrategy).reject(oldBooks::contains);