簡體   English   中英

Java 集合包含所有奇怪的行為

[英]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 操作獲得很大的性能提升,但不幸的是,這對我來說是不可能的。

這是錯誤的。 沒有hashCodeequals類從Object繼承它們,並且可以HashSet一起使用並且一切正常。 除了每個對象都是唯一的,這可能是意外和令人驚訝的,但 OP 的測試superList.containsAll(subList)做的完全一樣。

所以快速的解決方案是

new HashSet<>(superList).containsAll(subList)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM