Java SE 8 For the Really Impatient, Note 10
2,296 words in 14 minutes
Chapter 6 Concurrency Enhancements
ConcurentHashMap Improvements
mappingCount
: returns the size as a long
in case it’s too large.
An attacker can slow down a program by crafting a large number of strings that hash to the same value. As of Java 8, the concurrent hash map organizes the buckets as trees, not lists, when the key type implements Comparable
, guaranteeing O(log(n)) performance.
Updating Values
The following is not threadsafe
replace
: replacing a known old value with a new one.
You can also use a ConcurrentHashMap<String, AtomicLong>
or, with Java 8, a ConcurrentHashMap<String, LongAdder>
compute
: called with a key and a function to compute the new value. That function receives the key and the associated value, or null if there is none, and it computes the new value.
You cannot have null values in a ConcurrentHashMap
. There are many methods that use a null value as an indication that a given key is not present in the map.
computeIfPresent
: only computes a new value when there is already an old one.computeIfAbsent
: only computes a new value when there isn’t yet an old one.
A map of LongAdder
counters can be updated with
merge
: can do something special when a key is added for the first time. It has a particular parameter for the initial value that is used when the key is not yet present. Otherwise, the function that you supplied is called, combining the existing value and the initial value. (Unlike compute
, the function does not process the key.)
If the function that is passed to compute
or merge
returns null, the existing entry is removed from the map.
The function should not do a lot of work, otherwise other updates to the map may be blocked. And it should also not update other parts of the map.
Bulk Operations
The bulk operations travers the map and operate on the elements they find as they go along. No effort is made to freeze a snapshot of the map in time. Unless you happen to know that the map is not being modified while a bulk operation runs, you should treat its result as an approximation of the map’s state.
3 kinds of operations:
search
:applies a function to each key and/or value, until the function yields a non-null result.reduce
: combines all keys and/or values, using a provided accumulation function.forEach
: applies a function to all keys and/or values.
Each operation has 4 versions:
searchKeys
/reduceKeys
/forEachKey
: operates on keyssearchValues
/reduceValues
/forEachValue
: operates on valuessearch
/reduce
/forEach
: operates on keys and valuessearchEntries
/reduceEntries
/forEachEntry
: operates onMap.Entry
objects
You need to specify a parallelism threshold with each of the operations. If the map contains more elements than the threshold, the bulk operation is parallelized. If you want to run single thread, use a threshold of Long.MAX_VALUE
. If you want the max number of threads, use a threshold of 1.
Search
Find the first word that occurs more than 1000 times
The result is set to the first match, or to null if the search function returns null for all inputs.
2 variants of forEach
: the first one simply applies a consumer
function for each map entry:
The second takes an additional transformer
function, which is applied first, and its result is passed to the consumer:
The transformer can be used as a filter. Whenever the transformer returns null, the value is silently skipped.
reduce
: combines inputs with an accumulation function
You can also supply a transformer function.
The transformer can also be a filter.
The reduce
operation returns null, if the map is empty, or all entries have been filtered out. If there is only one element, its transformation is returned, and the accumulator is not applied.
There are specializations for int
, long
and double
outputs with suffix ToInt
, ToLong
, and ToDouble
. You need to transform the input to a primitive value and specify a default value and an accumulator function. The default value is returned when the map is empty.
These specializations act differently from the object versions where there is only one element to be considered. Instead of returning the transformed element, it is accumulated with the default. Therefore, the default must be the neutral element of the accumulator.
Set Views
The static newKeySet
method yields a Set<K>
that is actually a wrapper around a ConcurrentHashMap<Km, Boolean>
. (All map values are Boolean.TRUE
.)
keySet
: yields the set of keys. The set is mutable. If you remove the set’s elements, the keys(and their values) are removed from the map. But it doesn’t make sense to add elements to the key set, becase there would be no corresponding values to add. Java 8 adds a second keySet
method to ConcurrentHashMap
, with a default value, to be used when adding elements to the set:
If “Java” wasn’t already present in words, it now has a value of one.