Java Concurrent HashSet is equivalent to ConcurrentHashMap
I. Overview
In this tutorial, we'll see what is the relative equivalent of creating a thread-safe HashSet
case . Additionally, we will examine the pros and cons of each method.HashSet
ConcurrentHashMap
2. ConcurrentHashMap
Thread safety using factory methodsHashSet
First, we'll look at exposing static newKeySet()
methods. ConcurrentHashMap
Basically, this method returns an instance that respects java.util.Set
the interface and allows the use of standard methods like add(), contains(),
etc.
This can simply be created as:
Set<Integer> threadSafeUniqueNumbers = ConcurrentHashMap.newKeySet();
threadSafeUniqueNumbers.add(23);
threadSafeUniqueNumbers.add(45);
Also, Set
the performance returned is similar HashSet
, as both are implemented using hash-based algorithms. Also, the additional overhead from synchronization logic is minimal because the implementation uses ConcurrentHashMap
.
Finally, the downside is that this method only exists since Java 8 .
3. ConcurrentHashMap
Thread safety using instance methodsHashSet
So far, we've seen that ConcurrentHashMap.
Next, we'll deal ConcurrentHashMap
with instance methods that can be used to create thread-safe Set
instances. There are two methods available, newKeySet()
and newKeySet(defaultValue)
, which are slightly different from each other.
Both methods create a link to the original mapSet
. ConcurrentHashMap,
Set
In other words, the value is received every time we send to the original . Also, let's see the difference between these two methods.
3.1 newKeySet()
Methods
As mentioned above, the main difference between newKeySet()
exposing a map that contains all the keys of the original Set
newKeySet(defaultValue)
is that the current method does not support Set
adding new elements to it. So if we try to call add()
or addAll(),
we will get UnsupportedOperationException .
Although the operation of the remove(object)
or clear()
class works as expected, we need to note Set
that any changes on the or class will be reflected in the original map:
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);
Next is the output of the above code:
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)
Methods
Let's look at another way of using the keys in the map Set
compared to the one mentioned above, newKeySet(defaultValue)
returning an Set
instance that supports setting by calling or on set add()
oraddAll()
add()
Looking further at the default value passed as a parameter, this is used as the value or addAll()
method for each new entry added to the map . The following example shows how it works:
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);
Here is the output of the above code:
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
Thread safety using utility classesHashSet
Let's use the java.util.Collections
available synchronizedSet()
methods to create a thread-safe HashSet
instance:
Set<Integer> syncNumbers = Collections.synchronizedSet(new HashSet<>());
syncNumbers.add(1);
Before using this method, we need to realize that it is not as efficient as those discussed above . Basically just wrap the instance into a sync decorator ConcurrentHashMap
compared to implementing a low-level concurrency mechanism .synchronizedSet()
Set
5. Use CopyOnWriteArraySet
thread safetySet
Set
The last method implemented CopyOnWriteArraySet
. Creating Set
an instance of this is simple:
Set<Integer> copyOnArraySet = new CopyOnWriteArraySet<>();
copyOnArraySet.add(1);
Although using this class looks attractive, we need to consider some serious performance drawbacks. Behind the scenes, CopyOnWriteArraySet
use Array,
instead of HashMap,
to store data. This means that operations like contains()
or remove()
have O(n) complexity, whereas when using ConcurrentHashMap,
Set backed by , the complexity is O(1).
Set
Use this implementation when the size is generally kept small and read-only operations are the majority.
6 Conclusion
In this article, we saw the Set
different possibilities for creating thread-safe instances and highlighted the differences between them. First we saw ConcurrentHashMap.newKeySet()
static methods. HashSet
, this should be the first choice . After that we looked at static ConcurrentHashMap
methods andConcurrentHashMap
newKeySet(), newKeySet(defaultValue)
Finally we also discussed Collections.
synchronizedSet()
and CopyOnWriteArraySet
and there are performance drawbacks.
0 Comments