在标准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

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


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


当前回答

这应该在O(n)时间内完成。

public static <T> boolean isEqualCollection(Collection<T> c1, Collection<T> c2){
    if(nonNull(c1) && nonNull(c2)){
        Map<T, Long> c1Counts = c1.stream().collect(Collectors.groupingBy(i -> i, Collectors.counting()));
        for(T item : c2) {
            Long count  = c1Counts.getOrDefault(item, 0L);
            if(count.equals(0L)){
                return false;
            } else {
                c1Counts.put(item, count - 1L);
            }
        }
        return true;
    }
    return isNull(c1) && isNull(c2);
}

其他回答

我知道这是一个旧线程,但没有其他答案完全解决了我的用例(我猜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;
}

运行时间是O(n),因为我们对hashmap进行了O(2*n)次插入和O(3*n)次hashmap选择。我还没有完全测试这段代码,所以要小心:)

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

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

return list1.equals(list2);

这应该在O(n)时间内完成。

public static <T> boolean isEqualCollection(Collection<T> c1, Collection<T> c2){
    if(nonNull(c1) && nonNull(c2)){
        Map<T, Long> c1Counts = c1.stream().collect(Collectors.groupingBy(i -> i, Collectors.counting()));
        for(T item : c2) {
            Long count  = c1Counts.getOrDefault(item, 0L);
            if(count.equals(0L)){
                return false;
            } else {
                c1Counts.put(item, count - 1L);
            }
        }
        return true;
    }
    return isNull(c1) && isNull(c2);
}

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

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

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

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

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

list1.equals(list2);

如果列表包含自定义类MyClass,则该类必须重写equals函数。

 class MyClass
  {
  int field=0;
  @0verride
  public boolean equals(Object other)
        {
        if(this==other) return true;
        if(other==null || !(other instanceof MyClass)) return false;
        return this.field== MyClass.class.cast(other).field;
        }
  }

注意:如果你想在java.util. set而不是java.util. set上测试equals。列表,则对象必须覆盖hashCode函数。