【问题标题】:How to remove duplicates from a list?如何从列表中删除重复项?
【发布时间】:2011-02-20 10:10:03
【问题描述】:

我想从列表中删除重复项,但我的操作不起作用:

List<Customer> listCustomer = new ArrayList<Customer>();    
for (Customer customer: tmpListCustomer)
{
  if (!listCustomer.contains(customer)) 
  {
    listCustomer.add(customer);
  }
 }

【问题讨论】:

  • 你不应该在 Java 中编写这样的代码,就像你不应该在 Java 中编写自己的排序例程一样。如果它有能力通过 Set 之类的内置功能执行此操作,请使用它。

标签: java list collections duplicates


【解决方案1】:

假设您想保留当前订单而不想要Set,也许最简单的方法是:

List<Customer> depdupeCustomers =
    new ArrayList<>(new LinkedHashSet<>(customers));

如果要更改原始列表:

Set<Customer> depdupeCustomers = new LinkedHashSet<>(customers);
customers.clear();
customers.addAll(dedupeCustomers);

【讨论】:

  • 如果您没有覆盖对象(客户)的 equals 方法,HashSet 将比较对象在内存中的位置,因此它们将不相等,并且您的新 Set 中仍然会有重复项。
  • @GinjaNinja 有一个隐含的假设,即equals(和hashCode)以对类型有意义的方式实现。例如,LinkedHashSet&lt;JWindow&gt; 只会删除完全相同的对象的重复项,因为这就是 JWindow 实例相等的含义。
【解决方案2】:

如果您的问题中的代码不起作用,您可能没有在 Customer 类上正确实现 equals(Object)

大概有一些密钥(我们称之为customerId)可以唯一地识别客户;例如

class Customer {
    private String customerId;
    ...

equals(Object) 的适当定义如下所示:

    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Customer)) {
            return false;
        }
        Customer other = (Customer) obj;
        return this.customerId.equals(other.customerId);
    }

为了完整性,您应该还实现hashCode,以便两个相等的Customer 对象将返回相同的哈希值。与equals 的上述定义匹配的hashCode 将是:

    public int hashCode() {
        return customerId.hashCode();
    }

还值得注意的是,如果列表很大,这不是删除重复项的有效方法。 (对于有 N 个客户的列表,您需要在最坏的情况下执行 N*(N-1)/2 比较;即没有重复。)为了更有效的解决方案,您可以使用 HashSet 进行重复检查。另一种选择是使用 LinkedHashSet,如 Tom Hawtin 的 answer 中所述。

【讨论】:

    【解决方案3】:

    java 8 更新
    您可以使用如下数组流:

    Arrays.stream(yourArray).distinct()
                        .collect(Collectors.toList());
    

    【讨论】:

    • 最佳解决方案。使用一套你会失去订单(这可能是一个要求,但使用这个解决方案你不必考虑它)。
    • 你也可以使用 TreeSet,但你必须实现 Comparable,这并不总是可取的
    • 如何解决“类型不匹配:无法从 List 转换为 List”错误?
    • 我认为你可以在你的链中使用 map 方法关于Type Casting
    • Java 8 Lambda 简直太棒了,正是我们所要求的,必须每天使用 java。让我们的生活更轻松!
    【解决方案4】:

    客户是否执行equals() 合同?

    如果它没有实现equals()hashCode(),那么listCustomer.contains(customer) 将检查列表中是否已经存在完全相同的instance(我的意思是完全相同的实例)对象——内存地址等)。如果您正在寻找的是测试列表中是否已经有相同的客户(如果他们具有相同的客户名称或客户编号,则可能是相同的客户),那么您需要覆盖equals() 以确保它检查相关字段(例如客户名称)是否匹配。

    注意:如果您要覆盖equals(),请不要忘记覆盖hashCode()!否则,您可能会在使用 HashMap 和其他数据结构时遇到问题。为了更好地了解为什么会这样以及要避免哪些陷阱,请考虑查看 Josh Bloch 的 Effective Java 章节 equals()hashCode()(该链接仅包含有关为什么在实施时必须实施 hashCode() 的信息equals(),但是关于如何覆盖 equals() 也有很好的报道)。

    顺便问一下,你的套装有订购限制吗?如果没有,解决此问题的稍微简单的方法是使用Set&lt;Customer&gt;,如下所示:

    Set<Customer> noDups = new HashSet<Customer>();
    noDups.addAll(tmpListCustomer);
    return new ArrayList<Customer>(noDups);
    

    这会很好地为您删除重复项,因为集合不允许重复项。但是,这将丢失应用于tmpListCustomer 的任何排序,因为HashSet 没有明确的排序(您可以通过使用TreeSet 来解决这个问题,但这与您的问题并不完全相关)。这可以稍微简化您的代码。

    【讨论】:

    • +1 记住如果您需要维护秩序,则不能使用 Set。
    • 对于TreeSet&lt;&gt;()维护秩序的建议:+1
    【解决方案5】:

    列表→设置→列表(不同)

    只需将所有元素添加到Set:它不允许重复它的元素。如果您之后需要一个列表,请在之后使用新的 ArrayList(theSet) 构造函数(其中 theSet 是您的结果集)。

    【讨论】:

    • 使用 Set 将产生与上面编写的代码完全相同的结果,只是速度更快。海报说“不起作用”而不是“工作太慢”。
    • 很好 Set 可以工作,而他的代码却不行.
    • 我认为您假设他只想删除对同一对象的重复引用。如果是这种情况,那么发布的代码将起作用。
    • @fuzzy lollipop:Set 和他的代码完全一样。几乎可以肯定,这里的问题是 equals(Object) 和 hashCode() 方法;对象相等和值相等的区别。
    【解决方案6】:

    我怀疑您可能没有正确(或根本没有)实施Customer.equals()

    List.contains() 使用equals() 来验证它的任何元素是否与作为参数传递的对象相同。但是,equals 的默认实现测试的是物理身份,而不是值身份。因此,如果您没有在 Customer 中覆盖它,那么对于两个具有相同状态的不同 Customer 对象,它将返回 false。

    这里是how to implement equals(和hashCode,这是它的一对——如果你需要实现它们中的任何一个,你实际上必须始终实现它们)的细节。由于您没有向我们展示 Customer 类,因此很难给出更具体的建议。

    正如其他人所指出的,您最好使用 Set 而不是手动完成这项工作,但即便如此,您仍然需要实现这些方法。

    【讨论】:

    • 覆盖 java.lang.Object 中的 equals 和 hashCode 方法。你会想读这个:java.sun.com/developer/Books/effectivejava/Chapter3.pdf
    • 在 Java 中从列表中删除重复项的正确方法是使用 Set。而且你不能只覆盖equals()而不覆盖hashCode()。
    • @fuzzy lollipop:Set 并不神奇,当您没有编写代码告诉他们时,它无法检测到两个客户是否相等。使用 Set 将得到与发布的代码完全相同的结果,只是更快。
    • @fuzzy,当然,我只是在您撰写评论时添加了相关解释。你为什么要考虑这个单一的技术细节,虽然它当然很重要从长远来看 - 与解决具体问题没有直接关系?
    【解决方案7】:
    private void removeTheDuplicates(List<Customer>myList) {
        for(ListIterator<Customer>iterator = myList.listIterator(); iterator.hasNext();) {
            Customer customer = iterator.next();
            if(Collections.frequency(myList, customer) > 1) {
                iterator.remove();
            }
        }
        System.out.println(myList.toString());
    
    }
    

    【讨论】:

      【解决方案8】:

      “contains”方法搜索列表是否包含从 Customer.equals(Object o) 返回 true 的条目。如果您没有在 Customer 或其父对象之一中覆盖 equals(Object) ,那么它将仅搜索相同对象的现有出现。这可能是您想要的,在这种情况下您的代码应该可以工作。但是,如果您不希望有两个对象都代表同一个客户,那么您需要重写 equals(Object) 以在这种情况下返回 true。

      同样,使用 Set 的一种实现而不是 List 可以自动和更快地删除重复项(对于非常小的 List 之外的任何内容)。您仍然需要为 equals 提供代码。

      您还应该在覆盖 equals() 时覆盖 hashCode()。

      【讨论】:

      • 我没有投反对票,但我认为您建议覆盖 equals 以删除重复项可能会赢得它。
      • 您的意思是与接受的答案相同的建议?
      • @DJClayworth:在仔细阅读您的帖子后,我确实同意这是完全正确的(在我第一次阅读时,我虽然您建议做一个 special case 等于)。你会因为不公平的投票而得到我的 +1。另一方面,看看这里的其他帖子,有人一直在投反对票的复仇狂潮。
      【解决方案9】:

      几乎所有上述答案都是正确的,但我建议在创建相关列表时使用 Map 或 Set,而不是在获得性能之后。因为将列表转换为 Set 或 Map,然后再次将其重新转换为 List 是一项微不足道的工作。

      示例代码:

      Set<String> stringsSet = new LinkedHashSet<String>();//A Linked hash set 
      //prevents the adding order of the elements
      for (String string: stringsList) {
          stringsSet.add(string);
      }
      return new ArrayList<String>(stringsSet);
      

      【讨论】:

        【解决方案10】:

        两个建议:

        • 使用 HashSet 而不是 ArrayList。如果您的列表很长,这将大大加快 contains() 检查速度

        • 确保正确实现 Customer.equals() 和 Customer.hashCode(),即它们应该基于客户对象中基础字段的组合值。

        【讨论】:

          【解决方案11】:

          正如其他人所提到的,您可能没有正确实现 equals()。

          但是,您还应该注意,此代码被认为是非常低效的,因为运行时可能是元素数的平方。

          您可能需要考虑使用 Set 结构而不是 List,或者先构建 Set,然后将其转换为列表。

          【讨论】:

            【解决方案12】:

            最干净的方法是:

            List<XXX> lstConsultada = dao.findByPropertyList(YYY);
            List<XXX> lstFinal = new ArrayList<XXX>(new LinkedHashSet<GrupoOrigen>(XXX));
            

            并覆盖 hascodeequals 覆盖每个实体的 Id 属性

            【讨论】:

              【解决方案13】:

              恕我直言,这些天最好的方法:

              假设您有一个集合“dups”,并且您想要创建另一个包含相同元素但消除所有重复项的集合。以下单行代码可以解决问题。

              Collection<collectionType> noDups = new HashSet<collectionType>(dups);
              

              它通过创建一个集合来工作,根据定义,它不能包含重复项。

              基于 oracle 文档。

              【讨论】:

              • 为了补充这个答案,使用 Set 也会浪费大量空间,这里可以避免这种情况。
              【解决方案14】:

              Java 的正确答案是使用Set。如果你已经有一个List&lt;Customer&gt; 并且想要删除它

              Set<Customer> s = new HashSet<Customer>(listCustomer);
              

              否则只需直接使用Set 实现HashSetTreeSet 并跳过List 构造阶段。

              您还需要在 Set 中的域类上覆盖 hashCode() and equals(),以确保您想要的行为实际上是您得到的。 equals() 可以像比较对象的唯一 ID 一样简单,也可以像比较每个字段一样复杂。 hashCode() 可以像返回唯一 id'String 表示形式的 hashCode()hashCode() 一样简单。

              【讨论】:

              • 速度不如可维护性重要,您不必维护 Set 的代码,它是自记录的,并且是正确的 Java 习惯用法。
              • Homer:你可以用正确的方式、错误的方式或 Max Power 方式来做。巴特:什么是最大功率方式?荷马:这是错误的方式,只会更快。
              【解决方案15】:

              使用 java 8 流 api。

                  List<String> list = new ArrayList<>();
                  list.add("one");
                  list.add("one");
                  list.add("two");
                  System.out.println(list);
                  Collection<String> c = list.stream().collect(Collectors.toSet());
                  System.out.println(c);
              

              输出:

              值之前:[一,一,二]

              在值之后:[一,二]

              【讨论】:

              • 这不是原作者所问的。您正在制作一个集合而不是列表
              猜你喜欢
              • 1970-01-01
              • 2019-12-07
              • 2023-03-05
              • 1970-01-01
              • 1970-01-01
              • 2014-09-30
              • 1970-01-01
              • 2011-01-13
              相关资源
              最近更新 更多