[英]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.