Contents
1. Introduction
A HashMap is a map of keys to values which uses a hash table for implementation. The HashMap organizes the keys into buckets based on the value of the hashCode() of the key. It provides constant-time performance for get and put operations. What this means is that the time taken by get and put operations does not depend on the size of the map.
2. Creating a HashMap
Creating a HashMap is very simple.
HashMap hmap = new HashMap();
When you know you are going to store a large number of entries in the map, create it with a larger capacity. The capacity is automatically increased when the HashMap is nearly full. Increasing the capacity involves rehashing the table, so by specifying a larger capacity you can cut down on this cost. The following creates a HashMap with 200 buckets for storage.
HashMap hmap = new HashMap(200);
3. Iterate through a HashMap
Let us see what all methods are there to iterate over a HashMap.
Method 1: entrySet
The following code iterates over all the key-value pairs. The method entrySet() returns a Set of all mappings. From each mapping, you can get the key and value.
Map<String, String> map = new HashMap<>(); for (Map.Entry<String,String> entry : map.entrySet()) { System.out.println(entry.getKey() + " => " + entry.getValue()); }
Method 2: keySet
Or you could just get the keys and iterate over them, fetching the value from the map directly.
for (String key : map.keySet()) { System.out.println(key + " => " + map.get(key)); }
Method 3: values
Same for the values. Just fetch the values and loop over them.
for (String value : map.values()) { System.out.println(value); }
Method 4: forEach with lambda
A one line statement where you can also apply further operations such as filter, etc. Java 8 provides some pretty neat features, eh?
map.forEach((k, v) -> System.out.println(k + " => " + v));
Method 5: streams with entrySet
Iteration using java 8 streams, stream the entries. The advantage is you can apply the required streams operations in a single code block.
map .entrySet() .stream() .forEach(x -> System.out.println(x.getKey() + " => " + x.getValue()));
Method 6: streams with keySet
map .keySet() .stream() .forEach(x -> System.out.println(x + " => " + map.get(x)));
Method 7: streams with values
map .values() .stream() .forEach(System.out::println);
Method 8: Iterators
Before the advent of the java enhanced for-loop, this was the go-to method for iterating over a HashMap. Today with streams and the for-loop, this method is on the back burner.
Iterator<Map.Entry<String,String>> iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String,String> entry = iter.next(); System.out.println(entry.getKey() + " => " + entry.getValue()); }
4. Preserve Insertion Order
Iterating over an HashMap produces the entries in apparently random order. No guarantees are made about retrieval of the entries during iteration. When this consideration is important, you should know about the alternatives where you can make some sense of the ordering.
The LinkedHashMap is a specialization of the HashMap which guarantees that items are retrieved from the Map in insertion order.
HashMap<String,Float> map = new LinkedHashMap<>();
Here is an example which demonstrates the order:
Map<String,Float> map = new HashMap<>(); Files .lines(Paths.get("dist.all.last")) .limit(limit) .forEach(x -> { map.put(x.substring(0, 15).trim(), Float.parseFloat(x.substring(15, 20).trim())); }); map.forEach((k, v) -> System.out.printf("%-15s %.3f%n", k, v)); // prints (in unpredictable order): JOHNSON 0.810 JONES 0.621 MOORE 0.312 MILLER 0.424 SMITH 1.006 WILSON 0.339 TAYLOR 0.311 WILLIAMS 0.699 BROWN 0.621 DAVIS 0.480
Here is the same code with a LinkedHashMap:
HashMap<String,Float> map = new LinkedHashMap<>(); ... map.forEach((k, v) -> System.out.printf("%-15s %.3f%n", k, v)); // prints (in insertion order): SMITH 1.006 JOHNSON 0.810 WILLIAMS 0.699 JONES 0.621 BROWN 0.621 DAVIS 0.480 MILLER 0.424 WILSON 0.339 MOORE 0.312 TAYLOR 0.311
5. Searching a HashMap
5.1. Find a Key
Searching a HashMap for a key is straightforward. To search for a single key:
if ( map.containsKey("MOORE") ) { // found the key }
5.2. Find a Value
And so is searching for a value. If the map contains at least one mapping with the value, containsValue() returns true.
if ( map.containsValue(1.006) ) { // found at least one mapping }
5.3. Search for Multiple Values
While these methods are fine for looking for single keys or values, what if you want to find all mappings matching a certain key? Java 8 streams to the rescue. The following code prints all mappings where the value is greater than 0.5
map .entrySet() .stream() .filter(x -> x.getValue() > 0.5) .forEach(x -> System.out.printf("%-15s %.3f%n", x.getKey(), x.getValue()));
5.4. Search for Multiple Keys
How about searching for all keys that start with “J” and collecting the Set of mappings:
Set<Map.Entry<String,Float>> set = map .entrySet() .stream() .filter(x -> x.getKey().startsWith("J")) .collect(Collectors.toCollection(HashSet::new));
Summary
The HashMap is a very useful class in the JDK for storing a map of keys to values. It offers fast storage and retrieval operations. Entries can be retrieved from the HashMap with a variety of methods. Finally the LinkedHashMap offers retrieval of entries in the same order as the items were inserted.