在标准Java库中,找出两个list是否包含完全相同的元素的最简单方法是什么?

这两个list是否为相同实例并不重要,这两个list的类型参数是否不同也不重要。

e.g.

List list1
List<String> list2; 
// ... construct etc

list1.add("A");
list2.add("A"); 
// the function, given these two lists, should return true

我知道可能有什么东西在盯着我的脸:-)


编辑:为了澄清,我正在寻找完全相同的元素和元素的数量,按顺序。


当前回答

我的解决方案适用于不关心列表中的顺序的情况——换句话说:具有相同元素但顺序不同的列表将被认为具有相同的内容。

示例:["word1", "word2"]和["word2", "word1"]被认为内容相同。

我已经谈到了订购,我还需要说一些关于副本的事情。列表需要具有相同数量的元素才能被认为是相等的。

例如:["word1"]和["word1", "word1"]被认为不具有相同的内容。

我的解决方案:

public class ListUtil {

    public static <T> boolean hasSameContents(List<T> firstList, List<T> secondList) {      
        if (firstList == secondList) { // same object
            return true;
        }
        if (firstList != null && secondList != null) {
            if (firstList.isEmpty() && secondList.isEmpty()) {
                return true;
            }
            if (firstList.size() != secondList.size()) {
                return false;
            }
            List<T> tmpSecondList = new ArrayList<>(secondList);
            Object currFirstObject = null;
            for (int i=1 ; i<=firstList.size() ; i++) {
                currFirstObject = firstList.get(i-1);
                boolean removed = tmpSecondList.remove(currFirstObject);
                if (!removed) {
                    return false;
                }
                if (i != firstList.size()) { // Not the last element
                    if (tmpSecondList.isEmpty()) {
                        return false;
                    }
                }
            }
            if (tmpSecondList.isEmpty()) {
                return true;
            }
        }
        return false;
    }
}

我用Strings进行了测试,如下所示:

@Test public void testHasSameContents() throws Exception { // comparing with same list => no duplicate elements Assert.isTrue(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("one", "two", "three"))); // comparing with same list => duplicate elements Assert.isTrue(ListUtil.hasSameContents(List.of("one", "two", "three", "one"), List.of("one", "two", "three", "one"))); // compare with disordered list => no duplicate elements Assert.isTrue(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("three", "two", "one"))); // compare with disordered list => duplicate elements Assert.isTrue(ListUtil.hasSameContents(List.of("one", "two", "three", "one"), List.of("three", "two", "one", "one"))); // comparing with different list => same size, no duplicate elements Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("four", "five", "six"))); // comparing with different list => same size, duplicate elements Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "two"), List.of("one", "two", "three"))); Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("one", "two", "two"))); // comparing with different list => different size, no duplicate elements Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three", "four"), List.of("one", "two", "three"))); Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("one", "two", "three", "four"))); // comparing with different list => different sizes, duplicate elements Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three", "one"), List.of("one", "two", "three"))); Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("one", "two", "three", "one"))); }

其他回答

示例代码:

public static '<'T'>' boolean isListDifferent(List'<'T'>' previousList,
        List'<'T'>' newList) {

    int sizePrevoisList = -1;
    int sizeNewList = -1;

    if (previousList != null && !previousList.isEmpty()) {
        sizePrevoisList = previousList.size();
    }
    if (newList != null && !newList.isEmpty()) {
        sizeNewList = newList.size();
    }

    if ((sizePrevoisList == -1) && (sizeNewList == -1)) {
        return false;
    }

    if (sizeNewList != sizePrevoisList) {
        return true;
    }

    List n_prevois = new ArrayList(previousList);
    List n_new = new ArrayList(newList);

    try {
        Collections.sort(n_prevois);
        Collections.sort(n_new);
    } catch (ClassCastException exp) {
        return true;
    }

    for (int i = 0; i < sizeNewList; i++) {
        Object obj_prevois = n_prevois.get(i);
        Object obj_new = n_new.get(i);
        if (obj_new.equals(obj_prevois)) {
            // Object are same
        } else {
            return true;
        }
    }

    return false;
}

List上的equals方法可以做到这一点,列表是有序的,所以要相等,两个List必须具有相同的元素,且顺序相同。

return list1.equals(list2);

汤姆的回答很好,我完全同意他的回答!

这个问题的一个有趣方面是,您是否需要List类型本身及其固有顺序。

如果不是,你可以降级为Iterable或Collection,这让你在传递数据结构时具有一定的灵活性,这些数据结构是根据插入时间排序的,而不是在你想检查的时候排序。

如果顺序无关紧要(并且没有重复的元素),可以考虑使用Set。

如果顺序很重要,但由插入时间定义(并且没有重复),则考虑LinkedHashSet,它类似于TreeSet,但按插入时间排序(重复项不计算)。这也给了你O(1)个平摊访问,而不是O(log n)。

private boolean listHaveEqualObjects(List<?> list1, List<?> list2){
    return list1.containsAll(list2) && list2.containsAll(list1);

如果你关心顺序,那么只需使用equals方法:

list1.equals(list2)

来自javadoc:

Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order. This definition ensures that the equals method works properly across different implementations of the List interface.

如果你想检查与顺序无关,你可以复制所有的元素到set,并在结果集上使用equals:

public static <T> boolean listEqualsIgnoreOrder(List<T> list1, List<T> list2) {
    return new HashSet<>(list1).equals(new HashSet<>(list2));
}

这种方法的一个局限性是它不仅忽略了顺序,而且还忽略了重复元素的频率。例如,如果list1是["A", "B", "A"], list2是["A", "B", "B"],则Set方法将认为它们相等。

如果你需要对顺序不敏感,但对重复的频率敏感,你可以:

在比较它们之前对两个列表(或副本)进行排序,就像在回答另一个问题时所做的那样 或复制所有元素到Multiset