Even though Map is implemented by many classes, many of them are not thread-safe or some of them are but not efficient. This is why ConcurrentMap was introduced in Java 1.5. It is thread-safe and efficient.
Overridden default implementations:
- compute
- replaceAll
- forEach
- getOrDefault
- computerIfAbsent
- computerIfPresent
ConcurrentMap consists of an array of nodes that are represented as table buckets and they are initialised after the first insertion.
Efficiency comparison between ConcurrentMap and HashMap
- If one wants to operate on data as fast as possible, all threads must be used, which therefore means that ConcurrentMap is more effective here.
- If only a single thread access is required, HashMap is faster.
- add method is 3 times faster if implemented by HashMap.
- get method is faster if implemented by ConcurrentMap.
If a program requires multiple threads access, ConcurrentMap is the better option. However, if the program will only be using 1 thread, then HashMap would be the better option.
Methods in ConcurrentMap
- default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): Attempts to compute a mapping for the specified key and its current mapped value.
- default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction): If the key given as a parameter is not associated with a value (or null) , attempts to compute its value and enters it into this map unless null.
- default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
- default void forEach(BiConsumer<? super K, ? super V> action): performs the given action for each entry in the current map util all entries have been processed.
- default V getOrDefault(Object key, V defaultValue): returns the value which the specified key is mapped to or defaultValue (given as 2nd parameter) if the map contains no mapping for the key.
- default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction): if the key is not already associated with a value or is associated with null, then it associates it with the specified non-null value.
- V putIfAbsent(K key, V value): if the specified key has not been already associated with a value, associate it with the given value.
- boolean remove(Object key, Object value): removes the entry for a key only if currently mapped to a given value.
- V replace(K key, V value): replaces the entry for a key only if currently mapped to some value.
- boolean replace(K key, V oldValue, V newValue): replaces the entry for a key only if currently mapped to a given value.
For more information on the main methods of EnumSet, feel free to visit the original Oracle documentation.
Example program using some of the methods mentioned above
import java.util.concurrent.*; class ConcurrentHashMapExample { public static void main(String[] args) { ConcurrentHashMap conCurrHashMap = new ConcurrentHashMap(); conCurrHashMap.put(100, "Elephant"); conCurrHashMap.put(101, "Tiger"); conCurrHashMap.put(102, "Lion"); conCurrHashMap.put(103, "Cow"); // since 103 already exists, this won't work conCurrHashMap.putIfAbsent(103, "Goat"); conCurrHashMap.remove(103, "Goat"); System.out.println("After removal: " + conCurrHashMap); // since 103 was removed, this now works conCurrHashMap.putIfAbsent(103, "Leopard"); System.out.println("After put: " + conCurrHashMap); // changing Goat to Cheetah conCurrHashMap.replace(103, "Leopard", "Cheetah"); System.out.println("Final: " + conCurrHashMap); } }
Output:
After removal: {100=Elephant, 101=Tiger, 102=Lion, 103=Cow} After put: {100=Elephant, 101=Tiger, 102=Lion, 103=Cow} Final: {100=Elephant, 101=Tiger, 102=Lion, 103=Cow}