【问题标题】:Finding duplicates in a collection在集合中查找重复项
【发布时间】:2011-11-13 23:00:52
【问题描述】:

在集合中查找和标记重复对象的最佳方法是什么?假设我们有一个 List people,我们的重复策略是基于名字和姓氏的完全匹配。

  1. 识别所有重复项
  2. 标记每个重复的人,表明它是重复的
  3. 对于每个重复的人,确定它是重复的对象

有没有用番石榴做这个的简单方法?

【问题讨论】:

  • 你能澄清一下你的意思是“识别它是重复的对象”吗?我想你的意思是第一个项目是“真正的”项目,随后的项目是重复的?

标签: java guava


【解决方案1】:

你不需要 Guava 来做这个:

List<Person> people = ...
Map<Name, Person> peopleByName = new HashMap<>();
for (Person person : people) {
  // Name is a simple value class with equality based on its fields
  Name name = new Name(person.getFirstName(), person.getLastName());
  Person firstPersonWithName = peopleByName.get(name);
  if (firstPersonWithName == null) {
    peopleByName.put(name, person);
  } else {
    // or whatever you do to mark a duplicate
    person.setDuplicateOf(firstPersonWithName);
  }
}

也就是说,您可以使用 Guava Table 而不是 Map 并避免需要创建 Name...例如,使用名字作为行键和姓氏作为列键。

另一种选择是使用Multimaps.index 按姓名索引列表中的所有人员。然后,对于映射到特定名称的每个人员列表,第一个人将是您列表中具有该名称的第一个人,而其他人将是重复的。

【讨论】:

    【解决方案2】:

    为什么不尝试在 person 对象中覆盖 .equals() 。然后为每个人对象“duplicateOf”或其他内容添加一个新字段。

    然后循环遍历数组,检查每个人与其他人。如果人员 'duplicateOf' 字段为空,则跳过它。如果 .equals() 返回 true,您可以设置 'duplicateOf' 字段。

    【讨论】:

      【解决方案3】:

      您可以尝试使用Guava's TreeMultimap

      创建一个新的 TreeMultimap,使用比较器对其进行初始化,以便根据需要比较您:TreeMultimap.create(Comparator, Ordering.arbitrary())

      这是一个单元测试:

      package org.test.guava;
      
      import java.util.Arrays;
      import java.util.Comparator;
      import java.util.List;
      
      import org.junit.Test;
      
      import com.google.common.collect.Multimap;
      import com.google.common.collect.Ordering;
      import com.google.common.collect.TreeMultimap;
      
      public class GuavaTest {
      
          private static class Person {
              private String name;
      
              public Person(String name) {
                  this.name = name;
              }
      
              public String getName() {
                  return name;
              }
      
              @Override
              public String toString() {
                  return "Person [name=" + name + "]";
              }
      
          }
      
          @Test
          public void test() throws Exception {
              List<Person> persons = Arrays.asList(new Person("person1"), new Person("person2"), new Person("person1"));
              Comparator<Person> comparator = new Comparator<Person>() {
                  public int compare(Person o1, Person o2) {
                      return o1.getName().compareTo(o2.getName());
                  }
              };
      
              Multimap<Person, Person> groups = TreeMultimap.create(comparator, Ordering.arbitrary());
              for(Person person : persons) {
                  groups.put(person, person);
              }
      
              System.out.println(groups.asMap());
          }
      
      }
      

      【讨论】:

      • 我怎样才能满足我的问题中的要求 3?
      • 答案已更新。很抱歉用 multisets 误导你。请尝试使用 TreeMultimaps。
      • 使用排序的多重映射是不必要的,它使您的意图不太清楚,因为您只是使用它作为选择不同键相等的一种方式。最好使用名称本身作为键。然后你可以使用Multimaps.index
      • 但是,如果重复策略将使用目标类的多个字段,Multimaps.index 会更难使用,因为对于每个新的重复策略,我们也必须创建一个新的key 类。
      【解决方案4】:

      Person 类必须实现boolean equals(Object o)

      然后你可以通过这种方式找到重复项:

      你在某个地方:Collection&lt;Person&gt; list;

      Person[] persons = list.toArray();
      Integer[] duplicateOf = new Integer[persons.length];
      Arrays.fill(duplicateOf, -1);
      
      // For all the values in the Collection
      for (int i = 0; i < persons.length; i++) {
      
        // Find the duplicate
        for (int j = 0; j < persons.length; j++) {
          if (persons[i].equals(persons[j]) && i != j)
            duplicateOf[j] = i;
        }
      }
      

      现在您有了数组duplicateOf,您可以这样阅读:元素j 的副本位于索引duplicateOf[j]

      【讨论】:

        猜你喜欢
        • 2018-01-30
        • 2011-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多