Computer scienceProgramming languagesJavaWorking with dataCollectionsCollections framework

Map implementations

10 minutes read

We already have covered what is a Map interface and looked at its methods. There are also a number of implementations of Map interface. Now it is time to study them!

LinkedHashMap

The LinkedHashMap stores the order in which elements were inserted.

Let's see a part of the previous example again:

Map<Integer, String> products = new LinkedHashMap<>(); // ordered map of products

products.put(1000, "Notebook");
products.put(2000, "Phone");
products.put(3000, "Keyboard");

System.out.println(products); // it's always ordered {1000=Notebook, 2000=Phone, 3000=Keyboard}

In this code, the order of pairs is always the same and matches the order in which they are inserted into the map.

TreeMap

The TreeMap class represents a map that gives us guarantees on the order of the elements. It corresponds to the sorting order of the keys determined either by their natural order (if they implement the Comparable interface) or by specific Comparator implementation.

This class implements the SortedMap interface, which extends the base Map interface. It provides some new methods related to comparisons of keys:

  • Comparator<? super K> comparator() returns the comparator used to order elements in the map or null if the map uses the natural ordering of its keys;

  • E firstKey() returns the first (lowest) key in the map;

  • E lastKey() returns the last (highest) key in the map;

  • SortedMap<K, V> headMap(K toKey) returns a submap containing elements whose keys are strictly less than toKey;

  • SortedMap<K, V> tailMap(K fromKey) returns a submap containing elements whose keys are greater than or equal to fromKey;

  • SortedMap<K, V> subMap(K fromKey, K toKey) returns a submap containing elements whose keys are in range fromKey (inclusive) toKey (exclusive);

The example below demonstrates how to create and use an object of TreeMap. This map is filled with events, each of them has a date (key) and title (value).

LocalDate is a class that represents a date. The invocation of LocalDate.of(year, month, day) method creates the specified date object with the given year, month and day.

SortedMap<LocalDate, String> events = new TreeMap<>();

events.put(LocalDate.of(2017, 6, 6), "The Java Conference");
events.put(LocalDate.of(2017, 6, 7), "Another Java Conference");
events.put(LocalDate.of(2017, 6, 8), "Discussion: career or education?");
events.put(LocalDate.of(2017, 6, 9), "The modern art");
events.put(LocalDate.of(2017, 6, 10), "Coffee master class");

LocalDate fromInclusive = LocalDate.of(2017, 6, 8);
LocalDate toExclusive = LocalDate.of(2017, 6, 10);

System.out.println(events.subMap(fromInclusive, toExclusive));

The code outputs the resulting submap:

{2017-06-08=Discussion: career or education?, 2017-06-09=The modern art}

Use TreeMap only when you really need the sorting order of elements since this implementation is less efficient than HashMap.

Iterating over maps

It is impossible to directly iterate over a map since it does not implement the Iterable interface. Fortunately, some methods of maps return other collections which are iterable. The order of elements when iterating depends on the concrete implementation of the Map interface.

The following code shows how to get keys and values in a for-each loop:

Map<String, String> friendPhones = Map.of(
        "Bob", "+1-202-555-0118",
        "James", "+1-202-555-0220",
        "Katy", "+1-202-555-0175"
);

// printing names
for (String name : friendPhones.keySet()) {
    System.out.println(name);
}

// printing phones
for (String phone : friendPhones.values()) {
    System.out.println(phone);
}

If you want to print a key and its associated value at the same iteration, you can get entrySet() and iterate over it.

for (var entry : friendPhones.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

This code prints all pairs as we expect:

Bob: +1-202-555-0118
James: +1-202-555-0220
Katy: +1-202-555-0175

We use var released in Java 10 to declare the loop's variable entry, but it is not necessary. If you have an older version of Java or just don't want to use var, you can write the data type explicitly like Map.Entry<String, String>.

The same behavior can be achieved by using a lambda expression with two arguments if you prefer this way:

friendPhones.forEach((name, phone) -> System.out.println(name + ": " + phone));

Other collections as values

It is possible to store other collections as values in maps since collections are objects as well.

Here is an example with a map of synonyms:

Map<String, Set<String>> synonyms = new HashMap<>();

synonyms.put("Do", Set.of("Execute"));
synonyms.put("Make", Set.of("Set", "Attach", "Assign"));
synonyms.put("Keep", Set.of("Hold", "Retain"));

// {Keep=[Hold, Retain], Make=[Attach, Assign, Set], Do=[Execute]}
System.out.println(synonyms);

Storing collections as keys of a map, on the other hand, is not a common case, and it has some restrictions. Such keys should be represented by immutable collections. We will not consider this case here.

Map equality

Two maps are considered equal if they contain the same keys and values. Types of maps are not important.

So, the following maps are fully equal:

Map<String, Integer> namesToAges1 = Map.of("John", 30, "Alice", 28);
Map<String, Integer> namesToAges2 = new HashMap<>();

namesToAges2.put("Alice", 28);
namesToAges2.put("John", 30);

System.out.println(Objects.equals(namesToAges1, namesToAges2)); // true

But the following two maps are different since the second map does not include "Alice":

Map<String, Integer> namesToAges1 = Map.of("John", 30, "Alice", 28);
Map<String, Integer> namesToAges2 = Map.of("John", 30);

System.out.println(Objects.equals(namesToAges1, namesToAges2)); // false

By this, we are finishing our consideration of maps. There was a lot of theory. If there's something you don't understand, try to practice anyway and return to the theory when questions arise.

Conclusion

There are many implementations of Map inteface in Java, which backed by different principles. LinkedHashMap extends HashMap functionality by providing ordering. TreeMap uses tree as a backed data structure. Map is very agile and can store every class as a key or value.

8 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo