找到两个不同列表是否包含完全相同的元素的简单方法?

Gru*_*eck 232 java collections

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

如果两个列表是相同的实例,则无关紧要,如果列表的类型参数不同则无关紧要.

例如

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

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

我知道可能有些东西盯着我:-)


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


编辑:谢谢你指出我看不见的明显答案:-)

虽然到目前为止给出的所有答案都是正确的,但有些答案比其他答案更正确,所以在接受之前我会等待一段时间以获得最好的四舍五入的答案.

Lau*_*ves 339

如果您关心订单,那么只需使用equals方法:

list1.equals(list2)
Run Code Online (Sandbox Code Playgroud)

来自javadoc:

将指定对象与此列表进行比较以获得相等性.当且仅当指定的对象也是列表时,返回true,两个列表具有相同的大小,并且两个列表中的所有对应元素对都相等.(如果(e1 == null?e2 == null:e1.equals(e2)),则两个元素e1和e2相等.)换句话说,如果两个列表包含相同顺序的相同元素,则它们被定义为相等.此定义确保equals方法在List接口的不同实现中正常工作.

如果要独立于顺序进行检查,可以将所有元素复制到集合,并在结果集合上使用等于:

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

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

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

  • 如果你想独立于订单检查,你不能使用containsAll吗? (51认同)
  • @Dennis大小检查真的只有在你知道每个列表只包含不同的元素时才有效.例如,给定`a = [x,y,x]`和`b = [x,y,z]`然后大小相等,`b.containsAll(a)`将返回true,但是`b`包含一个不在'a`中的元素. (7认同)
  • 我不知道containsAll的实现细节,但似乎它可能是坏的.如果containsAll一遍又一遍地调用contains(),那么你将得到一个O(n ^ 2)alg.整体集合应为O(nlogn) (6认同)
  • 实际上,如果集合只是O(nlogn),另一种方法是在列表上调用Collections.sort(),然后使用equals.如果你想保留订单,你需要复制列表,这可能是昂贵的,并且有利于设置解决方案...所以你必须考虑你的情况:-). (6认同)
  • 就像对使用 `containsAll()` 的人的简短评论:请记住事先检查两个列表的大小是否相同!这允许您仅使用一次对 `containsAll()` 的调用。 (2认同)
  • 如果一个列表至少有一个重复值在另一个列表中不存在,则 Sets 解决方案会错误地将它们识别为相等。我错过了一些明显的东西吗? (2认同)

Tom*_*Tom 89

我在评论中发布了一些内容,我认为它保证了自己的答案.

正如大家在这里所说,使用equals()取决于顺序.如果您不关心订单,您有3个选择.

选项1

使用containsAll().在我看来,这个选项并不理想,因为它提供了最差的性能,O(n ^ 2).

选项2

这有两种变化:

2a)如果你不关心维护你的列表的顺序...... Collections.sort()在两个列表上使用.然后使用equals().这是O(nlogn),因为你做了两种排序,然后是O(n)比较.

2b)如果您需要维护列表的顺序,可以先复制两个列表.然后,您可以在复制的列表上使用解决方案2a.然而,如果复制非常昂贵,这可能没有吸引力.

这导致:

选项3

如果您的要求与第2b部分相同,但复制过于昂贵.您可以使用TreeSet为您进行排序.将每个列表转储到自己的TreeSet中.它将在集合中排序,原始列表将保持不变.然后equals()对两个TreeSets 进行比较.所述TreeSetss时,可以建在O(nlogn)时间,并且equals()为O(n).

请你选择:-).

编辑:我几乎忘记了 Laurence Gonsalves指出的同一个警告.TreeSet实现将消除重复.如果您关心重复项,则需要某种排序的多重集.

  • @laz:如果两个列表中的不同元素重复,则检查大小将不起作用.例如:[A,A,B] vs [A,B,B]大小相等. (7认同)
  • 包含所有我认为给出错误的答案,你需要包含所有两种方式.`a.containsAll(b)&& b.containsAll(a)` (7认同)
  • ContainsAll(),即使以两种方式调用,如果元素的频率很大,仍然不会给出正确的结果:`Arrays.asList(1,2,3,3).containsAll(Arrays.asList(1,2,2) ,3)) &amp;&amp; Arrays.asList(1,2,2,3).containsAll(Arrays.asList(1,2,3,3))` 给出 true 而不是 false。 (3认同)

meg*_*lop 19

如果您正在使用(或者很乐意使用)Apache Commons Collections,您可以使用CollectionUtils.isEqualCollection,如果给定的Collections包含具有完全相同基数的完全相同的元素,则返回true.


Rei*_*eus 12

聚会很晚但想要添加这个空的安全检查:

Objects.equals(list1, list2)
Run Code Online (Sandbox Code Playgroud)


小智 8

我知道这是一个旧线程,但其他答案都没有完全解决我的用例(我猜Guava Multiset也可以这样做,但这里没有例子).请原谅我的格式.我仍然很想在堆栈交换上发帖.另外,如果有任何错误,请告诉我

假设您有List<T>a和List<T>b,并且您想检查它们是否与以下条件相同:

1)O(n)的预计运行时间
2)相等,便定义为:对于a或b的所有元素时,发生的次数的元件在a等于b中发生元件的次数.元素相等定义为T.equals()

private boolean listsAreEquivelent(List<? extends Object> a, List<? extends Object> b) {
    if(a==null) {
        if(b==null) {
            //Here 2 null lists are equivelent. You may want to change this.
            return true;
        } else {
            return false;
        }
    }
    if(b==null) {
        return false;
    }
    Map<Object, Integer> tempMap = new HashMap<>();
    for(Object element : a) {
        Integer currentCount = tempMap.get(element);
        if(currentCount == null) {
            tempMap.put(element, 1);
        } else {
            tempMap.put(element, currentCount+1);
        }
    }
    for(Object element : b) {
        Integer currentCount = tempMap.get(element);
        if(currentCount == null) {
            return false;
        } else {
            tempMap.put(element, currentCount-1);
        }
    }
    for(Integer count : tempMap.values()) {
        if(count != 0) {
            return false;
        }
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

运行时间为O(n),因为我们正在将O(2*n)插入到散列映射中,并且O(3*n)散列映射选择.我还没有完全测试这段代码,所以要小心:)

//Returns true:
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("B","A","A"));
listsAreEquivelent(null,null);
//Returns false:
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("B","A","B"));
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("A","B"));
listsAreEquivelent(Arrays.asList("A","A","B"),null);
Run Code Online (Sandbox Code Playgroud)


dav*_*veb 6

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

return list1.equals(list2);
Run Code Online (Sandbox Code Playgroud)

  • 除非您对列表进行排序,否则它们不会排序。 (5认同)
  • 我的意思是,包含 {"A", "B"} 的列表和包含 {"B", "A"} 的列表与此方法不相等。这很可能就是我的意图,但我想确保没有人忽视它。 (4认同)
  • @mmyers:列表*中的项目不会排序,除非您对它们进行排序。列表本身具有隐式的项目排序(按索引),除非您更改列表中的项目,否则该排序不会更改。(与集合或集合相比,如果您迭代两次,则不能保证一致的排序) (3认同)

gra*_*mum 6

private boolean listHaveEqualObjects(List<?> list1, List<?> list2){
    return list1.containsAll(list2) && list2.containsAll(list1);
Run Code Online (Sandbox Code Playgroud)

  • 更好:list1.containsAll(list2) &amp;&amp; list2.size() == list1.size(); (2认同)

Lee*_*dor 5

尝试使用此版本,该版本不需要顺序相同,但支持具有多个相同值。仅当每个具有相同数量的任何值时,它们才匹配。

public boolean arraysMatch(List<String> elements1, List<String> elements2) {
    // Optional quick test since size must match
    if (elements1.size() != elements2.size()) {
        return false;
    }
    List<String> work = newArrayList(elements2);
    for (String element : elements1) {
        if (!work.remove(element)) {
            return false;
        }
    }
    return work.isEmpty();
}
Run Code Online (Sandbox Code Playgroud)