Map Implementations 2 – Collections: Part II
By Jaime Williams / December 23, 2022 / No Comments / Introduction to Streams, Oracle Certification Exam, Replacing Elements in Collections
If the weight group is not in the map, its frequency is set to 1; otherwise, the method reference Integer::sum increments the frequency by the given value 1. In both cases an appropriate entry for the weight group is put in the frequency map. Note the arguments passed to the method reference and the lambda expression: oldVal is the current value associated with the key and givenVal is the specified value, 1, passed to the merge() method.
Generic types guarantee that the keys and the values in the map are of the correct type, and autoboxing/unboxing of primitive values guarantees the correct type of an operand in an expression.
Some other strategies to update the frequency map are outlined here, but none is more elegant than the one with the merge() method.
The straightforward solution below requires an explicit null check to determine whether the value returned by the get() method is null, or risks a NullPointer-Exception at runtime when incrementing the null value in the frequency reference.
Integer frequency = groupFreqMap.get(weightGroup);
if (frequency == null) frequency = 0;
groupFreqMap.put(weightGroup, ++frequency);
Below, the getOrDefault() method never returns a null value, since it returns the associated frequency value or the specified default value 0 depending on whether the weight group is found or not found in the map, respectively. No explicit null check is necessary.
Integer frequency = groupFreqMap.getOrDefault(weightGroup, 0);
groupFreqMap.put(weightGroup, ++frequency);
The putIfAbsent() method puts an entry with frequency 0 for the weight group if it is not found so that the get() method will always return a non-null value which can be safely incremented.
groupFreqMap.putIfAbsent(weightGroup, 0);
Integer frequency = groupFreqMap.get(weightGroup);
groupFreqMap.put(weightGroup, ++frequency);
The compute() method always updates the value associated with the key if the two-arity function returns a non-null value, which is always the case below. The null check is now done in the lambda expression.
groupFreqMap.compute(weightGroup, (k, v) -> v == null ? 1 : v + 1);
- The program creates a sorted set of keys (which are weight groups) from the groupFreqMap at (6). The Map.keySet() method returns a set view of keys, which is passed as an argument to a TreeSet<E> to create a sorted set—in this case, the natural ordering of Integers is used.
TreeSet<Integer> sortedKeySet = new TreeSet<>(groupFreqMap.keySet()); // (6)
- The histogram is printed by implicitly iterating over the sorted key set at (7). Since a sorted set is an Iterable<E>, we can use the forEach() method that takes a consumer to print the histogram.
sortedKeySet.forEach(key -> // (7)
System.out.printf(“%5s: %s%n”, key,
String.join(“”, Collections.nCopies(groupFreqMap.get(key), “*”)))
For each key, the corresponding value (i.e., the frequency) is retrieved and converted to a string with the corresponding number of “*”. The method Collections.nCopies() creates a list with an equal number of elements as its first argument and where each element is the same as the second argument. The elements of this list are joined to create a string by the String.join() method with the first argument specifying the delimiter to use between the elements, which in this case is the empty string.
Alternately, a for(:) loop can be used to explicitly iterate over the sorted set view of the frequency map.
Example 15.10 Using Maps
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
public class Histogram {
public static void main(String[] args) {
System.out.println(“Data: ” + Arrays.toString(args));
// Create a map to store the frequency for each group.
Map<Integer, Integer> groupFreqMap = new HashMap<>(); // (1)
// Determine the frequencies:
for (String argument : args) { // (2)
double weight = Double.parseDouble(argument); // (3)
int weightGroup = (int) Math.round(weight/5.0)*5; // (4)
groupFreqMap.merge(weightGroup, 1, Integer::sum); // (5)
}
System.out.println(“Frequencies: ” + groupFreqMap);
// Create sorted key set.
TreeSet<Integer> sortedKeySet = new TreeSet<>(groupFreqMap.keySet()); // (6)
System.out.println(“Histogram:”);
// Implicit iteration over the key set.
sortedKeySet.forEach(key -> // (7)
System.out.printf(“%5s: %s%n”, key,
String.join(“”, Collections.nCopies(groupFreqMap.get(key), “*”)))
);
}
}
Running the program with the following arguments:
>
java Histogram 74 75 93 75 93 82 61 92 10 185
gives the following output:
Data: [74, 75, 93, 75, 93, 82, 61, 92, 10, 185]
Frequencies: {80=1, 185=1, 10=1, 90=1, 75=3, 60=1, 95=2}
Histogram:
10: *
60: *
75: ***
80: *
90: *
95: **
185: *