Collections.newSetFromMap(»ConcurrentHashMap«) vs. Collections.synchronizedSet(»HashSet«)
Solution 1
What you may be thinking of is
Set<Type> set = Collections.newSetFromMap(new ConcurrentHashMap<Type, Boolean>());
This supports concurrent updates and reads. Its Iterator won't throw ConcurrentModicationException. where as
Set<Type> set = Collections.synchronizedSet(new HashSet<Type());
Is more light weight but only allows one thread at a time to access the set. You need to lock the set explicitly if you want to Iterator over it and you can still get a CME if you don't update it in a safe way (while iterating over it)
Solution 2
The first one returns a Set that basically has the same thread-safe and performance guarantees as the map passed as argument. If the map isn't thread-safe, the set won't be either. You typically use this method to create a concurrent set from a concurrent map, because there is no ConcurrentHashSet in the API.
The second one returns a proxy to the given set, which has all its methods synchronized.
Solution 3
Actually you may get several Set thread-safe implementations.
I. Collections.synchronizedSet(new HashSet
I would not recommend this solution. It is itself thread-safe, must still be used with care in a concurrent environment. See:
Stack stack = new SynchronizedArrayStack(new ArrayStack());
...
// don't do this in a multi-threaded environment
if (!stack.isEmpty()) {
stack.pop(); // can throw IllegalStateException
}
As a result you must use client-side locking:
synchronized(stack) {
if (!stack.isEmpty()) {
stack.pop();
}
}
II. The second alternative concurrent implementation of Set interface - CopyOnWriteArraySet. However this solution should not be used in a context where you were expecting many searches or insertions. But iteration costs O(1) per element faster than HashSet and it has one advantage which is really compelling in some applications.
III. The last one uses the implementation of CuncurrentHashMap:
Collections.newSetFromMap(new ConcurrentHashMap<Type, Boolean>());
It uses implementations of java.util.concurrent.locks.Lock. The map divides itself into parts that can be separately locked, giving improved concurrency. So you should choose between the last two options.
Thera are also options for sorted set impelementations. I would recommend you to read Java Generics and Collections
chapter 11.5 Collections and Thread Safety
.
Solution 4
The Collections API leave the construction of the map to the client as below:
ConcurrentHashMap<String, Boolean> conMap = new ConcurrentHashMap<String, Boolean>();
Set<String> users = Collections.newSetFromMap(conMap);
System.out.println("Users: " + users);
users.add("Jon");//results in adding to the conMap instance
users.add("Tyron");
System.out.println("Users: " + users);
System.out.println("conMap = " + conMap);
Adding to the set also adds to the map
Users: [Tyron, Jon] conMap = {Tyron=true, Jon=true}
conMap.put("Jubin", Boolean.FALSE);
System.out.println("Users: " + users);
System.out.println("conMap = " + conMap);
Adding to the Map also results on adding to the Set
Users: [Tyron, Jubin, Jon] conMap = {Tyron=true, Jubin=false, Jon=true}
ConcurrentHashMap.newKeySet creates new HashMap with the KeySetView
ConcurrentHashMap.KeySetView<String, Boolean> keySetView = ConcurrentHashMap.newKeySet();
keySetView.add("Feba");
System.out.println("keySetView = " + keySetView);
System.out.println("keySetView.getMap() = " + keySetView.getMap());
keySetView = [Feba] keySetView.getMap() = {Feba=true}
keySetView.getMap().put("BeN",Boolean.TRUE);
System.out.println("keySetView = " + keySetView);
System.out.println("keySetView.getMap() = " + keySetView.getMap());
keySetView = [BeN, Feba] keySetView.getMap() = {BeN=true, Feba=true}
fbahr
Updated on June 03, 2022Comments
-
fbahr almost 2 years
Apparently, there are two ways to obtain a thread-safe HashSet instance using Java’s
Collections
utility class.I ask:
- How do they differ?
- Which, and under what circumstances, is to be preferred over the other?
-
fbahr almost 12 yearsDuh, my bad. First of all: I meant to write
Collections.newSetFromMap(»ConcurrentHashMap«)
- so, the question at hand: is the set obtained fromnewSetFromMap(...)
, using a map fromjava.util.concurrent
, still thread-safe? -
user207421 almost 12 years@fbahr As I said, check the Javadoc. It contains the answer.
-
user207421 almost 12 yearsI think you should reword 'only allows one thread to be reading or writing'.
-
David Bosschaert about 9 years... and the Javadoc says 'The resulting set displays the same ordering, concurrency, and performance characteristics as the backing map' so if you use it with a concurrent map, the set is also a concurrent set.
-
Ivan Voroshilin about 9 yearsVery sad, that guys who got up-votes answered incorrectly. David's questions is correct.