Java Concurrent HashSet 等價於 ConcurrentHashMap
- java
一、概述
在本教程中,我們將看到什麼是創建線程安全的HashSet
情況下,相對HashSet
什麼是等效的ConcurrentHashMap
。此外,我們將研究每種方法的優缺點。
2. 使用ConcurrentHashMap
工廠方法的線程安全HashSet
首先,我們將查看公開靜態newKeySet()
方法ConcurrentHashMap
基本上,此方法返回一個尊重java.util.Set
接口的實例,並允許使用標準方法,如add(), contains(),
等。
這可以簡單地創建為:
Set<Integer> threadSafeUniqueNumbers = ConcurrentHashMap.newKeySet();
threadSafeUniqueNumbers.add(23);
threadSafeUniqueNumbers.add(45);
此外,返回的Set
的性能類似於HashSet
,因為兩者都是使用基於哈希的算法實現的。此外,同步邏輯帶來的額外開銷也很小,因為實現使用了ConcurrentHashMap
。
最後,缺點是該方法僅從 Java 8 開始存在。
3. 使用ConcurrentHashMap
實例方法的線程安全HashSet
至此,我們已經了解了ConcurrentHashMap.
接下來,我們將處理可用於ConcurrentHashMap
的實例方法來創建線程安全的Set
實例。有兩種方法可用, newKeySet()
和newKeySet(defaultValue)
,它們彼此略有不同。
這兩種方法都創建了一個與原始地圖鏈接Set
。換句話說,每次我們向原始ConcurrentHashMap,
Set
都會接收該值。此外,讓我們看看這兩種方法之間的區別。
3.1 newKeySet()
方法
如上所述, newKeySet()
公開了一個包含原始映射的所有鍵的Set
newKeySet(defaultValue)
的主要區別在於當前方法不支持向Set
添加新元素。因此,如果我們嘗試調用add()
或addAll(),
我們將得到UnsupportedOperationException .
儘管remove(object)
或clear()
類的操作按預期工作,但我們需要注意Set
上的任何更改都將反映在原始映射中:
ConcurrentHashMap<Integer,String> numbersMap = new ConcurrentHashMap<>();
Set<Integer> numbersSet = numbersMap.keySet();
numbersMap.put(1, "One");
numbersMap.put(2, "Two");
numbersMap.put(3, "Three");
System.out.println("Map before remove: "+ numbersMap);
System.out.println("Set before remove: "+ numbersSet);
numbersSet.remove(2);
System.out.println("Set after remove: "+ numbersSet);
System.out.println("Map after remove: "+ numbersMap);
接下來是上面代碼的輸出:
Map before remove: {1=One, 2=Two, 3=Three}
Set before remove: [1, 2, 3]
Set after remove: [1, 3]
Map after remove: {1=One, 3=Three}
3.2. newKeySet(defaultValue)
方法
讓我們看看另一種使用地圖中的鍵Set
與上面提到的相比, newKeySet(defaultValue)
返回一個Set
實例,該實例支持通過調用 set 上的add()
或addAll()
進一步查看作為參數傳遞的默認值,這被用作地圖中添加的每個新條目的值add()
或addAll()
方法。以下示例顯示了它是如何工作的:
ConcurrentHashMap<Integer,String> numbersMap = new ConcurrentHashMap<>();
Set<Integer> numbersSet = numbersMap.keySet("SET-ENTRY");
numbersMap.put(1, "One");
numbersMap.put(2, "Two");
numbersMap.put(3, "Three");
System.out.println("Map before add: "+ numbersMap);
System.out.println("Set before add: "+ numbersSet);
numbersSet.addAll(asList(4,5));
System.out.println("Map after add: "+ numbersMap);
System.out.println("Set after add: "+ numbersSet);
下面是上面代碼的輸出:
Map before add: {1=One, 2=Two, 3=Three}
Set before add: [1, 2, 3]
Map after add: {1=One, 2=Two, 3=Three, 4=SET-ENTRY, 5=SET-ENTRY}
Set after add: [1, 2, 3, 4, 5]
4. 使用Collections
實用程序類的線程安全HashSet
讓我們使用java.util.Collections
可用synchronizedSet()
方法來創建一個線程安全的HashSet
實例:
Set<Integer> syncNumbers = Collections.synchronizedSet(new HashSet<>());
syncNumbers.add(1);
在使用這種方法之前,我們需要意識到它的效率不如上面討論的那些。與實現低級並發機制的ConcurrentHashMap
相比, synchronizedSet()
基本上只是將Set
實例包裝到同步裝飾器中。
5. 使用CopyOnWriteArraySet
線程安全Set
Set
實現的最後一種方法CopyOnWriteArraySet
。創建這個Set
的實例很簡單:
Set<Integer> copyOnArraySet = new CopyOnWriteArraySet<>();
copyOnArraySet.add(1);
儘管使用這個類看起來很有吸引力,但我們需要考慮一些嚴重的性能缺陷。在幕後, CopyOnWriteArraySet
使用Array,
而不是HashMap,
來存儲數據。這意味著像contains()
或remove()
這樣的操作有 O(n) 的複雜度,而當使用由ConcurrentHashMap,
支持的 Set 時,複雜度是 O(1)。
Set
大小通常保持較小且只讀操作佔多數時使用此實現。
6 結論
在本文中,我們看到了創建線程安全Set
實例的不同可能性,並強調了它們之間的區別。首先我們看到了ConcurrentHashMap.newKeySet()
靜態方法。 HashSet
時,這應該是首選。之後我們查看了ConcurrentHashMap
靜態方法和用於ConcurrentHashMap
實例的newKeySet(), newKeySet(defaultValue)
最後我們還討論了Collections.
synchronizedSet()
和CopyOnWriteArraySet
並且存在性能缺陷。