In some situations, you need to store pairs of associated objects. For example, when counting the number of words in a text, the first one is a word, and the second one is the number of its occurrences in the text. There is a special type of collection called map to effectively store such pairs of objects.
A map is a collection of key-value pairs. Keys are always unique, while values can repeat.
A good example of a map from the real world is a phone book where keys are names of your friends and values are phone numbers associated with them.
Keys : Values
-----------------------
Bob : +1-202-555-0118
James : +1-202-555-0220
Katy : +1-202-555-0175
Maps have some similarities with sets and arrays;
keys of a map form a set, but each key has an associated value;
keys of a map are similar to indexes of an array, but the keys can have any type including integer numbers, strings and so on.
Due to these reasons, you can encounter some kind of deja vu effect when learning maps.
Next, all our examples will use string and numbers as keys since using custom classes as types of keys have some significant points the same as for sets. It will be considered in other topics.
The Map interface
The Collections Framework provides the Map<K,V> interface to represent a map as an abstract data type. Here, K is a type of keys, and V is a type of associated values. The Map interface is not a subtype of the Collection interface, but maps are often considered as collections since they are part of the framework.
The interface declares a lot of methods to work with maps. Some of the methods are similar to methods of Collection, while others are unique to maps.
1) Collection-like methods:
int size()returns the number of elements in the map;boolean isEmpty()returnstrueif the map does not contain elements andfalseotherwise;void clear()removes all elements from the map.
We hope these methods do not need any comments.
2) Keys and values processing:
V put(K key, V value)associates the specifiedvaluewith the specifiedkeyand returns the previously associated value with thiskeyornull;V get(Object key)returns the value associated with the key, ornullotherwise;V remove(Object key)removes the mapping for akeyfrom the map;boolean containsKey(Object key)returnstrueif the map contains the specifiedkey;boolean containsValue(Object value)returnstrueif the map contains the specifiedvalue.
These methods are similar to the methods of collections, except they process key-value pairs.
3) Advanced methods:
V putIfAbsent(K key, V value)puts a pair if the specified key is not already associated with a value (or is mapped tonull) and returnnull, otherwise, returns the current value;V getOrDefault(Object key, V defaultValue)returns the value to which the specified key is mapped, ordefaultValueif this map contains no mapping for the key.
These methods, together with some others, are often used in real projects.
4) Methods which return other collections:
Set<K> keySet()Returns aSetview of the keys contained in this map;Collection<V> values()returns aCollectionview of the values contained in this map;Set<Map.Entry<K, V>> entrySet()returns aSetview of the entries (associations) contained in this map.
This is not even a complete list of methods since Map is a really huge interface. The documentation really helps when using maps.
To start using a map, you need to instantiate one of its implementations: HashMap, TreeMap, and LinkedHashMap. They use different rules for ordering elements and have some additional methods. There are also immutable maps.
Immutable maps
The simplest way to create a map is to invoke the of method of the Map interface. The method takes zero or any even number of arguments in the format key1, value1, key2, value2, ... and returns an immutable map.
Map<String, String> emptyMap = Map.of();
Map<String, String> friendPhones = Map.of(
"Bob", "+1-202-555-0118",
"James", "+1-202-555-0220",
"Katy", "+1-202-555-0175"
);
Now let's consider some operations that can be applied to immutable maps using our example with friendPhones.
The size of a map equals the number of pairs contained in it.
System.out.println(emptyMap.size()); // 0
System.out.println(friendPhones.size()); // 3
It is possible to get a value from a map by its key:
String bobPhone = friendPhones.get("Bob"); // +1-202-555-0118
String alicePhone = friendPhones.get("Alice"); // null
String phone = friendPhones.getOrDefault("Alex", "Unknown phone"); // Unknown phone
Note that the getOrDefault method provides a simple way to prevent NullPointerException since it avoids null's.
It is also possible to check whether a map contains a particular key or value by using containsKey and containsValue methods.
We can directly access the set of keys and collection of values from a map:
System.out.println(friendPhones.keySet()); // [James, Bob, Katy]
System.out.println(friendPhones.values()); // [+1-202-555-0220, +1-202-555-0118, +1-202-555-0175]
Since it is immutable, only methods that do not change the elements of this map will work. Others will throw an exception UnsupportedOperationException. If you'd like to put or remove elements, use one of HashMap, TreeMap or LinkedHashMap. We will consider them in the next topic.
HashMap
The HashMap class represents a map backed by a hash table. This implementation provides constant-time performance for get and put methods assuming the hash function distributes the elements uniformly among the buckets.
The following example demonstrates a map of products where the key is the product code, and the value is the name.
Map<Integer, String> products = new HashMap<>();
products.put(1000, "Notebook");
products.put(2000, "Phone");
products.put(3000, "Keyboard");
System.out.println(products); // {2000=Phone, 1000=Notebook, 3000=Keyboard}
System.out.println(products.get(1000)); // Notebook
products.remove(1000);
System.out.println(products.get(1000)); // null
products.putIfAbsent(3000, "Mouse"); // it does not change the current element
System.out.println(products.get(3000)); // Keyboard
This implementation is often used in practice since it is highly-optimized for putting and getting pairs.
Conclusion
The Map interface is one of the most useful and popular features in Java. It implements the idea of an associative array. There are a number of analogues in the real world, such as phone books, dictionaries, and others. Map is often treated as a part of collection, although it doesn't inherit the Collection interface. The easiest way to create a map is with the Map.of(..) construct, which creates an immutable map where entries cannot be changed after initialization. One of the most popular implementaion of a mutable map is the HashMap.