Java HashMap Examples

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.