The Map interface has many methods similar to the methods of List and Set:
- int size()
- void clear()
- int hashCode()
- boolean isEmpty()
- boolean equals(Object o)
- default void forEach(BiConsumer<K,V> action)
- Static factory methods: of(), of(K k, V v), of(K k1, V v1, K k2, V v2), and many other methods
The Map interface, however, does not extend Iterable, Collection, or any other interface for that matter. It is designed to be able to store values by their keys. Each key is unique, while several equal values can be stored with different keys in the same map. The combination of key and value constitutes an Entry, which is an internal interface of Map. Both value and key objects must implement equals() method. A key object must also implement hashCode() method.
Many methods of Map interface have exactly the same signature and functionality as in the interfaces List and Set, so we are not going to repeat them here. We will only walk through the Map-specific methods:
- V get(Object key): Retrieves the value by the provided key; returns null if there is no such key
- Set<K> keySet(): Retrieves all the keys from the map
- Collection<V> values(): Retrieves all values from the map
- boolean containsKey(Object key): Returns true if the provided key exists in the map
- boolean containsValue(Object value): Returns true if the provided value exists in the map
- V put(K key, V value): Adds the value and its key to the map; returns the previous value stored with the same key
- void putAll(Map<K,V> m): Copies from the provided map all the key-value pairs
- default V putIfAbsent(K key, V value): Stores the provided value and maps to the provided key if such a key is not already used by the map; returns the value mapped to the provided key—either the existing or the new one
- V remove(Object key): Removes both key and value from the map; returns value or null if there is no such a key or the value is null
- default boolean remove(Object key, Object value): Removes the key-value pair from the map if such a pair exists in the map
- default V replace(K key, V value): Replaces the value if the provided key is currently mapped to the provided value; returns the old value if it was replaced; otherwise, returns null
- default boolean replace(K key, V oldValue, V newValue): Replaces the value oldValue with the provided newValue if the provided key is currently mapped to the oldValue; returns true if the oldValue was replaced; otherwise, returns false
- default void replaceAll(BiFunction<K,V,V> function): Applies the provided function to each key-value pair in the map and replaces it with the result, or throws an exception if not possible
- Set<Map.Entry<K,V>> entrySet(): Returns a set of all key-value pairs as the objects of Map.Entry
- default V getOrDefault(Object key, V defaultValue): Returns the value mapped to the provided key or the defaultValue if the map does not have the provided key
- static Map.Entry<K,V> entry(K key, V value): Returns an unmodifiable Map.Entry object with the provided key and value in it
- static Map<K,V> copy(Map<K,V> map): Converts the provided Map into an unmodifiable one
The following Map methods are much too complicated for the scope of this book, so we just mention them for the sake of completeness. They allow combining or calculating multiple values and aggregating them in a single existing value in the Map or creating a new one:
- default V merge(K key, V value, BiFunction<V,V,V> remappingFunction): If the provided key-value pair exists and the value is not null, the provided function is used to calculate a new value; removes key-value pair if the newly calculated value is null; if the provided key-value pair does not exist or the value is null, the provided non-null value replaces the current one; this method can be used for aggregating several values; for example, it can be used for concatenating the String values: map.merge(key, value, String::concat); we will explain what String::concat means in Chapter 13, Functional Programming
- default V compute(K key, BiFunction<K,V,V> remappingFunction): Computes a new value using the provided function
- default V computeIfAbsent(K key, Function<K,V> mappingFunction): Computes a new value using the provided function only if the provided key is not already associated with a value or the value is null
- default V computeIfPresent(K key, BiFunction<K,V,V> remappingFunction): Computes a new value using the provided function only if the provided key is already associated with a value and the value is not null
This last group of computing and merging methods is rarely used. The most popular by far are the V put(K key, V value) and V get(Object key) methods, which allow the use of the main Map function of storing key-value pairs and retrieving the value using the key. The Set<K> keySet() method is often used for iterating over the map's key-value pairs, although the entrySet() method seems a more natural way of doing that. Here is an example:
Map<Integer, String> map = Map.of(1, "s1", 2, "s2", 3, "s3");
for(Integer key: map.keySet()){
System.out.print(key + ", " + map.get(key) + ", ");
//prints: 3, s3, 2, s2, 1, s1,
}
for(Map.Entry e: map.entrySet()){
System.out.print(e.getKey() + ", " + e.getValue() + ", ");
//prints: 2, s2, 3, s3, 1, s1,
}
The first of the for loops in the preceding code example uses the more widespread way to access the key-pair values of a map by iterating over the keys. The second for loop iterates over the set of entries, which in our opinion is a more natural way to do it. Notice that the printed out values are not in the same order we have put them in the map. That is because, since Java 9, the unmodifiable collections (that is what of() factory methods produce) have added randomization of the order of Set elements. It changes the order of the elements between different code executions. Such a design was done to make sure a programmer does not rely on a certain order of Set elements, which is not guaranteed for a set.