【问题标题】:Cross compare ArrayList elements and remove duplicates交叉比较 ArrayList 元素并删除重复项
【发布时间】:2012-02-12 03:17:07
【问题描述】:

我有一个ArrayList<MyObject>,它可能(或可能不)包含MyObject 的重复项,我需要从列表中删除。如果我要在两个 for 循环中迭代列表并交叉检查每个项目与其他项目,我该如何做到这一点,而不必检查重复两次。

我只需要检查每个项目一次,所以比较 A:B 就足够了 - 我不想再次比较 B:A,因为我已经这样做了。

此外;我可以在循环时从列表中删除重复项吗?还是会以某种方式破坏列表和我的循环?

编辑:好的,我忘记了查看第一个答案的重要部分:MyObjectduplicate 不仅仅意味着以 Java 方式表示 Object.equals(Object),但我需要能够使用我自己的算法比较对象,因为MyObjects 的相等性是使用一种算法计算的,该算法以我需要实现的特殊方式检查对象的字段!

此外,我不能只在 MyObject 中覆盖 euqals,因为有几种不同的算法可以实现不同的策略来检查两个 MyObjects 的相等性 - 例如有一个简单的HashComparer 和一个更复杂的EuclidDistanceComparer,两者都是AbstractComparerspublic abstract boolean isEqual(MyObject obj1, MyObject obj2); 实现不同的算法

【问题讨论】:

  • 带有自定义比较器的 TreeSet 应该可以解决问题。

标签: java for-loop arraylist comparison


【解决方案1】:

对列表进行排序,重复项将彼此相邻,便于识别和删除。只需通过列表记住上一项的值,以便您可以将其与当前项进行比较。如果它们相同,则删除当前项。

如果你使用普通的for-loop 遍历列表,你可以控制当前位置。这意味着当您删除一个项目时,您可以减少位置 (n--),以便下一次循环访问相同的位置(现在将是下一个项目)。

您需要在排序中提供自定义比较吗?这并不难:

Collections.sort(myArrayList, new Comparator<MyObject>() {

    public int compare(MyObject o1, MyObject o2) {
        return o1.getThing().compareTo(o2.getThing());
    }
});

我写了这个例子,所以getThing().compareTo() 代表你想要做的任何事情来比较这两个对象。如果它们相同,则必须返回一个整数,如果 o1 大于 o2 则返回大于 1,如果 o1 小于 o2 则返回 -1。如果getThing() 返回了StringDate,那么您已经准备就绪,因为这些类已经有了compareTo 方法。但是您可以将所需的任何代码放入自定义Comparator

【讨论】:

  • 这看起来不错 - 但我只能决定对象是否相同 - 如果它们不同,我应该返回什么? -1 还是 1?
  • 您在对象之间进行的比较是什么?
  • 我取决于 o1 是否应该在 o2 之前。请参阅比较器的 javadoc。
  • @ante - 这里只需要相同的对象彼此相邻。看问题! :)
  • @ante 我确实理解这个概念——我只是无法决定一个对象应该放在另一个之前还是之后,因为在我的情况下排序问题是无效的。只有平等与否——我无法回答“之前还是之后?”的问题。
【解决方案2】:

创建一个集合,如果顺序不重要,它会自动为您删除重复项。

Set<MyObject> mySet = new HashSet<MyObject>(yourList);

【讨论】:

    【解决方案3】:

    实例化一个新的基于集合的集合 HashSet。不要忘记为 MyObject 实现 equals 和 hashcode。

    祝你好运!

    【讨论】:

      【解决方案4】:

      或者http://docs.oracle.com/javase/6/docs/api/java/util/SortedSet.html如果你需要排序顺序..

      编辑:从http://docs.oracle.com/javase/6/docs/api/java/util/TreeSet.html 派生怎么样,它将允许您在构建时传入比较器。您覆盖 add() 以使用您的 Comparator 而不是 equals() - 这将使您能够灵活地创建根据您的 Comparator 排序的不同集合,它们将实施您的“平等”策略。

      别忘了equals()hashCode()...

      【讨论】:

        【解决方案5】:

        如果对象顺序无关紧要

        如果顺序不重要,可以将列表的元素放入Set

        Set<MyObject> mySet = new HashSet<MyObject>(yourList);
        

        重复的将被自动删除。

        如果对象顺序很重要

        如果排序很重要,那么您可以手动检查重复项,例如使用这个 sn-p:

        // Copy the list.
        ArrayList<String> newList = (ArrayList<String>) list.clone();
        
        // Iterate
        for (int i = 0; i < list.size(); i++) {
            for (int j = list.size() - 1; j >= i; j--) {
                // If i is j, then it's the same object and don't need to be compared.
                if (i == j) {
                    continue;
                }
                // If the compared objects are equal, remove them from the copy and break
                // to the next loop
                if (list.get(i).equals(list.get(j))) {
                    newList.remove(list.get(i));
                    break;
                }
                System.out.println("" + i + "," + j + ": " + list.get(i) + "-" + list.get(j));
            }
        }
        

        这将删除所有重复项,将最后一个重复值保留为原始条目。此外,它只会检查每个组合一次。

        使用 Java 8

        Java Streams 让它更加优雅:

        List<Integer> newList = oldList.stream()
            .distinct()
            .collect(Collectors.toList());
        

        如果您需要根据自己的定义考虑两个对象相等,您可以执行以下操作:

        public static <T, U> Predicate<T> distinctByProperty(Function<? super T, ?> propertyExtractor) {
            Set<Object> seen = ConcurrentHashMap.newKeySet();
            return t -> seen.add(propertyExtractor.apply(t));
        }
        

        Stuart Marks

        然后你可以这样做:

        List<MyObject> newList = oldList.stream()
            .filter(distinctByProperty(t -> {
                // Your custom property to use when determining whether two objects
                // are equal. For example, consider two object equal if their name
                // starts with the same character.
                return t.getName().charAt(0);
            }))
            .collect(Collectors.toList());
        

        此外

        Iterator(通常在 for-each 循环中使用)在数组中循环时不能修改列表。这将抛出一个ConcurrentModificationException。如果您使用 for 循环对其进行循环,则可以修改该数组。然后你必须控制迭代器的位置(在删除条目时递减它)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-07-15
          • 1970-01-01
          • 2014-12-16
          • 2017-12-31
          • 1970-01-01
          • 1970-01-01
          • 2013-02-02
          相关资源
          最近更新 更多