交叉比较ArrayList元素并删除重复项

时间:2012-01-17 09:55:56

标签: java for-loop arraylist comparison

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

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

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

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

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

5 个答案:

答案 0 :(得分:4)

对列表进行排序,重复项将彼此相邻,使其易于识别和删除。只需浏览列表,记住上一个项目的值,以便将其与当前项目进行比较。如果它们相同,请删除当前项目。

如果您使用普通的for循环遍历列表,则可以控制当前位置。这意味着当你删除一个项目时,你可以减少位置(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 :(得分:4)

创建一个集合,如果排序不重要,它将自动删除重复项。

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

答案 2 :(得分:2)

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

祝你好运!

答案 3 :(得分:1)

如果对象顺序无关紧要

如果订单不重要,您可以将列表的元素放入Set

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

重复项将自动删除。

如果对象顺序很重要

如果订购很重要,那么您可以手动检查重复项,例如使用此代码段:

// 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());

Futhermore

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

答案 4 :(得分:0)

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()以使用比较器代替equals() - 这将使您可以灵活地创建根据比较器订购的不同套件,并且他们将实施您的“平等” - 策略。

不要忘记equals()hashCode()虽然......