HashSet是一個用於存儲惟一元素的集合。java
在本文中,咱們將討論java.util.HashSet 類中removeAll()方法 的性能。git
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 將被中刪除。性能
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
爲了查看以上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 時,則性能很好。
在本文中,咱們看到了HashSet中removeAll()的性能。當原集合的元素少於集合的元素時,removeAll()的性能取決於集合的contains()方法的時間複雜度。
最後,本文的完整代碼可 在 GitHub 上找到。