containsAll
的{{3}}(official documentation)仅表示"如果此列表包含指定集合的所有元素,则返回true。"。但是,我刚测试了这个:
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(1);
List<Integer> list2 = new ArrayList<>();
list2.add(2);
list2.add(1);
list2.add(2);
System.out.println(list1.containsAll(list2));
结果为true
,即使list1
不包含第二个2
。
那么containsAll
的官方完全定义的行为是什么?它是否表现为从两个列表中删除了所有重复项?我记得在某个地方读过它可能会导致重复问题,但我不知道确切的情况。
答案 0 :(得分:3)
List.containsAll
方法的行为与文档一样:如果给定集合的所有元素都属于此集合,则返回true
,否则返回false
。文档没有说明元素的顺序或基数。
containsAll
方法在Collection
接口中声明,并在List
和Set
接口中重新声明,但它首先在Collection
接口中实现{3}}类的{3}}层次结构,如下所示:
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
据我所知,这个实现是由Java Collections框架中实现AbstractCollection
接口的大多数常见类继承的,除了CopyOnWriteArrayList
类和其他专用类,例如空列表和检查和不可变的包装等。
因此,如果您查看代码,您会发现它符合您引用的文档:
如果此列表包含指定集合的所有元素,则返回
true
。
在Collection
方法的文档中,还有@implSpec
标记,其中包含以下内容:
@implSpec
此实现迭代指定的集合,依次检查迭代器返回的每个元素以查看它是否包含在此集合中。如果包含所有元素,则返回
true
,否则返回false
。
关于可能的优化,它们全部转发到contains
方法的不同实现,这也由AbstractList.containsAll
以类似于暴力的方式实现。但是,contains
在AbstractCollection
中被覆盖以利用散列,也在HashSet
中,它使用索引等。
答案 1 :(得分:0)
您可以遍历一个列表并按值从另一个列表中删除元素,然后检查另一个列表大小是否为 == 0。如果是,则意味着所有第二个列表元素出现在第一个列表中的次数至少与第二个列表。
public boolean containsAll(List<Character> source, List<Character> target) {
for (Character character : source) {
target.remove(character);
if (target.isEmpty()) {
return true;
}
}
return target.size() == 0;
}
如果列表很大,HashMap 会更有效率
public static boolean containsAll(List<Character> source, List<Character> target) {
Map<Character, Long> targetMap = target.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
for (Character character : source) {
Long count = targetMap.get(character);
if (count != null) {
if (count > 1) {
targetMap.put(character, --count);
} else {
targetMap.remove(character);
}
}
}
return targetMap.isEmpty();
}