【问题标题】:Comparing two lists and removing duplicates from one比较两个列表并从中删除重复项
【发布时间】:2011-04-05 17:46:26
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 那么,您是如何检查图书平等 atm 的?
  • 如果你知道判断重复的逻辑,那么你应该能够使用相同的逻辑覆盖equals方法。
  • 如果书籍相等的定义不是普遍的,你将如何普遍地判断书籍是否是重复的?将其定义为覆盖等于与以其他方式定义它有何不同?

标签: java collections duplicates equals


【解决方案1】:

您可以将TreeSet 与自定义Comparator<Book> 一起使用:

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

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

【讨论】:

  • 我想过这一点,但使用比较器感觉“错误”,因为它们在语义上应该用于对对象进行排序,而不是测试相等的替代定义..
  • 是的,但约定是当且仅当对象相等时比较器返回 0,因此它意味着相等性测试。
  • 是的,没错。这绝对是最优雅的解决方案。
【解决方案2】:

为了让新书与众不同:

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

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

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

【讨论】:

  • 嗯,这是一个有趣的解决方案,绝对不是我会想到的。
【解决方案3】:

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

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

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

接下来,您将使用 distinct() 删除 newBooks 中的重复项,并使用 UnifiedSetWithHashingStrategy 删除列表中的重复项。

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 的提交者。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 2014-12-16
    • 2013-02-02
    • 2021-12-09
    • 1970-01-01
    • 2020-10-16
    • 1970-01-01
    相关资源
    最近更新 更多