[英]Java Collections containsAll Weired Behavior
我有以下代码,我在其中使用 superList 和 subList,我想检查 subList 实际上是 superList 的 subList。
我的对象没有实现 hashCode 或 equals 方法。 我在测试中创造了类似的情况。 当我运行测试时,结果显示 JDK 集合和普通集合的结果之间的性能差异非常大。运行测试后,我得到以下输出。
使用 Java Collection API 的时间流逝 8953 MilliSeconds & 结果为真 使用 Commons Collection API 的时间流逝 78 MilliSeconds & Result 为真
我的问题是为什么 java collection 在处理 containsAll 操作时如此缓慢。 我在那里做错了吗? 我无法控制从遗留代码中获得的集合类型。 我知道如果我对 superList 使用 HashSet 那么我会使用 JDK containsAll 操作获得很大的性能提升,但不幸的是,这对我来说是不可能的。
package com.mycompany.tests;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Before;
import org.junit.Test;
public class CollectionComparison_UnitTest {
private Collection<MyClass> superList = new ArrayList<MyClass>();
private Collection<MyClass> subList = new HashSet<MyClass>(50000);
@Before
public void setUp() throws Exception {
for (int i = 0; i < 50000; i++) {
MyClass myClass = new MyClass(i + "A String");
superList.add(myClass);
subList.add(myClass);
}
@Test
public void testIt() {
long startTime = System.currentTimeMillis();
boolean isSubList = superList.containsAll(subList);
System.out.println("Time Lapsed with Java Collection API "
+ (System.currentTimeMillis() - startTime)
+ " MilliSeconds & Result is " + isSubList);
startTime = System.currentTimeMillis();
isSubList = CollectionUtils.isSubCollection(subList, superList);
System.out.println("Time Lapsed with Commons Collection API "
+ (System.currentTimeMillis() - startTime)
+ " MilliSeconds & Result is " + isSubList);
}
}
class MyClass {
String myString;
MyClass(String myString) {
this.myString = myString;
}
String getMyString() {
return myString;
}
}
不同的算法:
ArrayList.containsAll()
提供O(N*N) ,而CollectionUtils.isSubCollection()
提供O(N+N+N) 。
您至少应该以相反的顺序尝试测试。 你的结果很可能只是表明 JIT 编译器做得很好:-)
ArrayList.containsAll
继承自AbstractCollection.containsAll
并且是一个简单的循环检查行中的所有元素。 每一步都是一个缓慢的线性搜索。 我不知道CollectionUtils
是如何工作的,但是比使用简单循环要快得多并不难。 将第二个 List 转换为HashSet
是一个肯定的胜利。 对两个列表进行排序并并行处理它们可能会更好。
CollectionUtils 源代码清楚地说明了这一点。 他们将两个集合都转换为“基数映射”,这是许多操作的一种简单而通用的方法。 在某些情况下,这可能不是一个好主意,例如,当第一个列表为空或非常短时,您实际上浪费了时间。 在你的情况下,与 AbstractCollection.containsAll 相比,这是一个巨大的胜利,但你可以做得更好。
OP写道
我知道如果我对 superList 使用 HashSet 那么我会使用 JDK containsAll 操作获得很大的性能提升,但不幸的是,这对我来说是不可能的。
这是错误的。 没有hashCode
和equals
类从Object
继承它们,并且可以与HashSet
一起使用并且一切正常。 除了每个对象都是唯一的,这可能是意外和令人惊讶的,但 OP 的测试superList.containsAll(subList)
做的完全一样。
所以快速的解决方案是
new HashSet<>(superList).containsAll(subList)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.