Java 中 HashSet 的 removeAll 性能分析


1. 概述

HashSet是一個用於存儲惟一元素的集合。java

在本文中,咱們將討論java.util.HashSet 類中removeAll()方法 的性能。git

2. HashSet.removeAll()

HashSet 的 removeAll 方法刪除全部包含指定集合的元素:github

Set<Integer> set = new HashSet<Integer>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);

Collection<Integer> collection = new ArrayList<Integer>();
collection.add(1);
collection.add(3);

set.removeAll(collection);

Integer[] actualElements = new Integer[set.size()];
Integer[] expectedElements = new Integer[] { 2, 4 };
assertArrayEquals(expectedElements, set.toArray(actualElements));

結果,原集合裏的元素 1 和 3 將被中刪除。性能

3. 內部實現和時間複雜度

removeAll()方法會先肯定哪一個集合更小,集合大小不一樣,執行邏輯不一樣。這是經過在原集合和指定集合上調用size() 方法來完成的。測試

若是指定集合的元素少於指定原的元素,則它以時間複雜度O( n )迭代指定的集合。它還檢查元素是否存在於時間複雜度爲 O(1) 的集合中。若是元素存在,則使用集合的remove()方法將其從原集合中 刪除,該方法的時間複雜度爲 O(1)。因此總的時間複雜度是 O( n )code

若是原集合的元素少於指定集合,則它使用 O( n )迭代此原集合。而後它經過調用其contains()方法檢查指定集合中是否存在每一個元素。若是存在這樣的元素,則從原集合中刪除該元素。因此這取決於contains()方法的時間複雜度。orm

如今在這種狀況下,若是指定集合是一個ArrayList,contains()方法的時間複雜度是 O( m )。所以,從集合HashSet中刪除ArrayList 中存在的全部元素的整體時間複雜度爲 O( n * m )rem

若是指定集合再次是HashSet,則contains()方法的時間複雜度爲 O(1)。所以,從集合HashSet中刪除HashSet 中存在的全部元素的整體時間複雜度爲 O( n )get

4. 性能

爲了查看以上3種狀況的性能差別,咱們來編寫一個簡單的JMH基準測試。hash

對於第一種狀況,咱們將初始化原集合和指定集合,其中原集合中的元素多於指定集合。在第二種狀況下,指定集合中的元素多於原集合。在第三種狀況下,第二個集合的元素數量比第一個集合多:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5)
public class HashSetBenchmark {

    @State(Scope.Thread)
    public static class MyState {
        private Set employeeSet1 = new HashSet<>();
        private List employeeList1 = new ArrayList<>();
        private Set employeeSet2 = new HashSet<>();
        private List employeeList2 = new ArrayList<>();
        private Set<Employee> employeeSet3 = new HashSet<>();
        private Set<Employee> employeeSet4 = new HashSet<>();

        private long set1Size = 60000;
        private long list1Size = 50000;
        private long set2Size = 50000;
        private long list2Size = 60000;
        private long set3Size = 50000;
        private long set4Size = 60000;

        @Setup(Level.Trial)
        public void setUp() {
            // populating sets
        }
    }
}

以後,咱們添加咱們的基準測試:

@Benchmark
public boolean given_SizeOfHashsetGreaterThanSizeOfCollection_whenRemoveAllFromHashSet_thenGoodPerformance(MyState state) {
    return state.employeeSet1.removeAll(state.employeeList1);
}

@Benchmark
public boolean given_SizeOfHashsetSmallerThanSizeOfCollection_whenRemoveAllFromHashSet_thenBadPerformance(MyState state) {
    return state.employeeSet2.removeAll(state.employeeList2);
}

@Benchmark
public boolean given_SizeOfHashsetSmallerThanSizeOfAnotherHashSet_whenRemoveAllFromHashSet_thenGoodPerformance(MyState state) {
    return state.employeeSet3.removeAll(state.employeeSet4);
}

結果以下:

Benchmark                                              Mode  Cnt            Score            Error  Units
HashSetBenchmark.testHashSetSizeGreaterThanCollection  avgt   20      2700457.099 ±     475673.379  ns/op
HashSetBenchmark.testHashSetSmallerThanCollection      avgt   20  31522676649.950 ± 3556834894.168  ns/op
HashSetBenchmark.testHashSetSmallerThanOtherHashset    avgt   20      2672757.784 ±     224505.866  ns/op

咱們能夠看到當HashSet的元素少於Collection 時,HashSet.removeAll() 的表現很是糟糕,Collection做爲參數傳遞給removeAll()方法。可是當另外一個集合再次是HashSet 時,則性能很好。

5. 結論

在本文中,咱們看到了HashSet中removeAll()的性能。當原集合的元素少於集合的元素時,removeAll()的性能取決於集合的contains()方法的時間複雜度。

最後,本文的完整代碼可 在 GitHub 上找到

參考: https://www.baeldung.com/java...

相關文章
相關標籤/搜索