“Don’t judge each day by the harvest you reap but by the seeds that you plant.”
― Robert Louis Stevenson
Check out the previous parts of this Java Collections Guide: Introduction, Part 1 – Collection, Part 2 – Sets and Part 3 – ArrayList.
Contents
1. Introduction
The HashMap implements the Map interface and provides the abstraction of a dictionary mapping of keys to values. It is a part of the Java Collections Framework but does not fall under the Iterable and Collection hierarchy. In this article, we learn how to use HashMaps.
In a previous guide, we covered some aspects of a HashMap usage including creation, iteration and search. In this article, we present some more HashMap use cases including parsing a CSV into a HashMap as well as a multi-map.
2. Adding Entries to HashMap
Quite simple. Use put().
Map<String,Integer> namefreq = new HashMap<>(); namefreq.put("Petra", 14); namefreq.put("Mario", 11); namefreq.put("Kasandra", 23); namefreq.forEach((k, v) -> System.out.printf("%s => %s%n", k, v)); // prints: Kasandra => 23 Petra => 14 Mario => 11
Note that there is no ordering of items on iteration since we are using a HashMap. If you need predictable iteration order, use a LinkedHashMap.
3. Parse CSV to a HashMap
And here is a slightly more complex example of a Map in use. We parse a CSV and create a Map of names to counts. (For the curious, the data comes from US Census data; it is a table of name, state and frequency count). The sample data looks like this:
Id,Name,Year,Gender,State,Count 1,Mary,1910,F,AK,14 2,Annie,1910,F,AK,12 3,Anna,1910,F,AK,10 4,Margaret,1910,F,AK,8 5,Helen,1910,F,AK,7 ...
The code reads lines in a stream, skips the header line, splits the line into fields, filters the row for a state (“CA
“) and female names (“F
“). Finally the data is collected into a HashMap of name to frequency count.
Pattern pattern = Pattern.compile(","); String csvFile = "StateNames.csv"; try (BufferedReader in = new BufferedReader(new FileReader(csvFile));){ Map<String,Integer> namefreq = in .lines() .skip(1) .map(x -> pattern.split(x)) .filter(x -> x[4].equals("CA") && x[3].equals("F")) .collect(HashMap::new, (map, x) -> map.put(x[1], Integer.parseInt(x[5])), Map::putAll); namefreq.forEach((k, v) -> System.out.println(k + " => " + v)); } // prints: Kasandra => 23 Petra => 14 Mario => 11 Nichelle => 6 Debbi => 46 Karly => 7 ...
4. Store a Map Inside a Map (Multi-Map)
A multi-map is a map of a key to a value which is itself a map. It is useful for storing multiple levels of data. We use this in the following example to store a map of (year => count) for each name. So the mapping follows a structure of (name => (year => count)).
The sample data is the same CSV file shown in the above example.
Pattern pattern = Pattern.compile(","); String csvFile = "StateNames.csv"; try (BufferedReader in = new BufferedReader(new FileReader(csvFile));){ Map<String,Map<Integer,Integer>> namefreq = in .lines() .skip(1) .map(x -> pattern.split(x)) .filter(x -> x[4].equals("CA")) .collect(HashMap::new, (map, x) -> map.compute(x[1], (k, v) -> { if ( v == null ) v = new HashMap<Integer,Integer>(); v.put(Integer.parseInt(x[2]), Integer.parseInt(x[5])); return v; }), Map::putAll); namefreq.forEach((k, v) -> { System.out.println(k + ":"); v.forEach((kk, vv) -> { System.out.println(" " + kk + " => " + vv); }); }); }
As before, the code streams lines from a CSV file, splits the line into fields, applies a filter to select the state (“CA
“) and collects the values in a multi-map. The multi-map is updated using the Map’s compute() method which applies a function to the specified mapping as follows
map.compute(key, <function to compute value>);
The function (which is specified as a lambda) is invoked with the currently existing (key, value) and is expected to update the value to the required value. Here we create a new HashMap of (year => count) and add the mapping. The returned HashMap is then stored as the value for the key.
map.compute(key, (key, value) -> { if ( value == null ) value = new HashMap<Integer,Integer>(); value.put(<year>, <count>); return value; });
4.1. Iterating over the Multi-Map
How can we loop through the outer map as well as the map contained within this map? Quite simple. Use a forEach() inside a forEach(). In the output below, we can see that the name “Leigh
” has multiple mappings inside the map.
namefreq.forEach((k, v) -> { System.out.println(k + ":"); v.forEach((kk, vv) -> { System.out.println(" " + kk + " => " + vv); }); }); // prints: Nichelle: 2007 => 6 Bilal: 1996 => 14 Bowen: 1970 => 6 ... Mario: 1987 => 11 Leigh: 1941 => 5 1974 => 51 1999 => 6 Santa: 1979 => 11
Summary
We examined some usage scenarios of the HashMap including parsing CSV and storing a multi-map.
See Also
- Java Collections Introduction gives an overall picture of the most important classes and interfaces.
- Java Collections – Part 1 covers iterating over collections and addition & removal of items.
- Java Collections – Sets explains Set operations and examples for HashSet, LinkedHashSet and TreeSet.
- Java Collections – ArrayList presents ArrayList-specific API methods.
- Java HashMap Examples demonstrates some examples for creating and iterating a HashMap.
- Java Collections – HashMap presents parsing a CSV to a HashMap as well as a multi-map.