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 並且存在性能缺陷。