Map in Java
Do not miss this exclusive book on Binary Tree Problems. Get it now for free.
Reading time: 20 minutes
A map
contains values on the basis of key, i.e. key and value pair. Each key and value pair is known as an entry. A Map contains unique keys.
A Map is useful if you have to search, update or delete elements on the basis of a key.
The java.util.Map interface represents a mapping between a key and a value. The Map interface is not a subtype of the Collection interface. Therefore it behaves a bit different from the rest of the collection types.
Few characteristics of the Map Interface are:
A Map cannot contain duplicate keys and each key can map to at most one value. Some implementations allow null key and null value like the HashMap and LinkedHashMap, but some do not like the TreeMap.
The order of a map depends on specific implementations, e.g TreeMap and LinkedHashMap have predictable order, while HashMap does not.
There are two interfaces for implementing Map in java: Map and SortedMap, and three classes: HashMap, TreeMap and LinkedHashMap.
Important points :
The Map interface maps unique keys to values. A key is an object that you use to retrieve a value at a later date.
- Given a key and a value, you can store the value in a Map object. After the value is stored, you can retrieve it by using its key.
- Several methods throw a NoSuchElementException when no items exist in the invoking map.
- A ClassCastException is thrown when an object is incompatible with the elements in a map.
- A NullPointerException is thrown if an attempt is made to use a null object and null is not allowed in the map.
- An UnsupportedOperationException is thrown when an attempt is made to change an unmodifiable map.
Why and When to use Maps :
Maps are perfect to use for key-value association mapping such as dictionaries. The maps are used to perform lookups by keys or when someone wants to retrieve and update elements by keys.
Some examples are:
- A map of error codes and their descriptions.
- A map of zip codes and cities.
- A map of managers and employees. Each manager (key) is associated with a list of employees (value) he manages.
- A map of classes and students. Each class (key) is associated with a list of students (value).
Java Map Hierarchy
There are two interfaces for implementing Map in java: Map and SortedMap, and three classes: HashMap, LinkedHashMap, and TreeMap.
A Map doesn't allow duplicate keys, but you can have duplicate values. HashMap and LinkedHashMap allow null keys and values, but TreeMap doesn't allow any null key or value.
A Map can't be traversed, so you need to convert it into Set using keySet() or entrySet() method.
i. HashMap
: It is the implementation of Map, but it doesn't maintain any order.
ii. LinkedHashMap
: It is the implementation of Map. It inherits HashMap class. It maintains insertion order.
iii. TreeMap
: It is the implementation of Map and SortedMap. It maintains ascending order.
Map Methods
Let’s have a look at some of the important Map methods.
int size()
: returns the number of key-value mappings in this Map.boolean isEmpty()
: returns true if there are no mappings present, otherwise false.boolean containsValue(Object value)
: returns true if there are at least one key mapped to the specified value, otherwise false.V get(Object key)
: returns the value mapped to the given key, if no mapping found then returns null.V put(K key, V value)
: adds the mapping of key-value pair to the map. If there is already a value mapped to this key, then replace the value. This method returns the previous value associated with key, or null if there was no mapping for key.V remove(Object key)
: Removes the mapping for a key from this map if it is present. Returns the value to which this map previously associated the key, or null if the map contained no mapping for the key.void putAll(Map<? extends K, ? extends V> m)
: Copies all of the mappings from the specified map to this map.void clear()
: removes all the mappings from the Map.Set<K> keySet()
: returns the Set view of all the keys in the Map. This key set is backed by Map, so any modifications to Map will be reflected to the key set and vice versa.Collection<V> values()
: returns the collection view of all the values in the Map. This collection is backed by Map, so any change in Map will reflect to this values collection and vice versa.Set<Map.Entry<K, V>> entrySet()
: returns the Set view of the mappings in the Map. This Set is backed by Map, so any modifications in Map will be reflected in the entry set and vice versa.
Map Implementations
Since Map is an interface you need to instantiate a concrete implementation of the Map interface in order to use it. The Java Collections API contains the following Map implementations:
-
java.util.HashMap
-
java.util.Hashtable
-
java.util.EnumMap
-
java.util.IdentityHashMap
-
java.util.LinkedHashMap
-
java.util.Properties
-
java.util.TreeMap
-
java.util.WeakHashMap
The most commonly used Map implementations areHashMap
andTreeMap
.
Each of these Map implementations behaves a little differently with respect to the order of the elements when iterating the Map, and the time (big O
notation) it takes to insert and access elements in the maps. -
HashMap maps a key and a value. It does not guarantee any order of the elements stored internally in the map.
-
TreeMap also maps a key and a value. Furthermore it guarantees the order in which keys or values are iterated - which is the sort order of the keys or values. Check out the Java Map JavaDoc for more details.
Map Example
Let’s have a look at a simple program for Java Map example. We will use Map implementation class HashMap for our example program.
package com.journaldev.examples;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class MapExample {
public static void main(String[] args) {
Map<String, String> data = new HashMap<>();
data.put("A", "A"); // put example
data.put("B", "B");
data.put("C", "C");
data.put("D", null); // null value
data.put(null, "Z"); // null key
String value = data.get("C"); // get example
System.out.println("Key = C, Value = " + value);
value = data.getOrDefault("E", "Default Value");
System.out.println("Key = E, Value=" + value);
boolean keyExists = data.containsKey(null);
boolean valueExists = data.containsValue("Z");
System.out.println("keyExists= " + keyExists + ", valueExists= " + valueExists);
Set<Entry<String, String>> entrySet = data.entrySet();
System.out.println(entrySet);
System.out.println("data map size=" + data.size());
Map<String, String> data1 = new HashMap<>();
data1.putAll(data);
System.out.println("data1 mappings= " + data1);
String nullKeyValue = data1.remove(null);
System.out.println("data1 null key value = " + nullKeyValue);
System.out.println("data1 after removing null key = " + data1);
Set<String> keySet = data.keySet();
System.out.println("data map keys = " + keySet);
Collection<String> values = data.values();
System.out.println("data map values = " + values);
data.clear();
System.out.println("data map is empty =" + data.isEmpty());
}
}
Output
Key = C, Value = C
Key = E, Value=Default Value
keyExists= true, valueExists= true
[null=Z, A=A, B=B, C=C, D=null]
data map size=5
data1 mappings= {null=Z, A=A, B=B, C=C, D=null}
data1 null key value = Z
data1 after removing null key = {A=A, B=B, C=C, D=null}
data map keys = [null, A, B, C, D]
data map values = [Z, A, B, C, null]
data map is empty =true
Inserting Elements Into a Java Map
To add elements to a Map you call its put() method. Here are a few examples:
Map mapA = new HashMap();
mapA.put("key1", "element 1");
mapA.put("key2", "element 2");
mapA.put("key3", "element 3");
The three put() calls maps a string value to a string key. You can then obtain the value using that key, as we will see in the next section.
Only Objects Can Be Inserted :
Only Java objects can be used as keys and values in a Java Map. In case you pass primitive values (e.g. int, double etc.) to a Map as key or value, the primitive values will be auto-boxed before being passed as parameters. Here is an example of auto-boxing primitive parameters passed to the put() method:
mapA.put("key", 123);
The value passed to the put() method in the above example is a primitive int. Java auto-boxes it inside an Integer instance though, because the put() method requires an Oject instance as both key and value. Auto-boxing would also happen if you passed a primitive as key to the put() method.
Subsequent Inserts With Same Key
A given key can only occur in a Java Map one time
. That means, that only a single key + value pair for each key can exist in the Map at the same time. In other words, for the key "key1" only one value can be stored in the same Map instance. Of course you can store values for the same key in different Map instances.
If you call put() more than once with the same key, the latest value passed to put() for that key will overwrite what is already stored in the Map for that key. In other words, the latest value replaces the existing value for the given key.
Null Keys Are Not Allowed
Note, that the key cannot be null. The Map uses the hashCode()
and equals()
methods of the key, to store the key + value pair internally, so if the key is null, the Map cannot place the key + value pair correctly internally.
Null Values Are Allowed
The value of a key
or value pair stored in a Map is allowed to be null - so this is perfectly valid:
mapA.put("D", null);
Note : you will get a null out when you call get() later with that key - so this will return null:
Object value = mapA.get("D");
The value variable will have the value null after this code has been executed, if a null value was inserted for this key earlier (like in the previous example).
Inserting All Elements From Another Map
The Java Map interface has a method called putAll() which can copy all key + value pairs (entries) from another Map instance into itself. In set theory, this is also referred to as the union of the two Map instances.
Here is an example of copying all entries from one Java Map into another via putAll():
Map mapA = new HashMap();
mapA.put("key1", "value1");
mapA.put("key2", "value2");
Map mapB = new HashMap();
mapB.putAll(mapA);
After running this code the Map referenced by variable mapB will contain both of the key + value entries inserted into mapA at the beginning of the code example.
The copying of entries only goes one way. Calling mapB.putAll(mapA) will only copy entries from mapA into mapB, not from mapB into mapA. To copy entries the other way, you would have to execute the code mapA.putAll(mapB).
Get Elements From a Java Map
To get a specific element stored in a Java Map you call its get() method, passing along the key for that element as parameter. Here is an example of getting a value stored in a Java Map:
String element1 = (String) mapA.get("key1");
Notice : the get() method returns a Java Object, so we have to cast it to a String (because we know the value is a String). Later in this Java Map tutorial you will see how to use Java Generics to type the Map so it knows what specific key and value types it contains. This makes type casting unnecessary, and makes it harder to insert the wrong values into the Map by accident.
Get or Default Value
The Java Map interface has a getOrDefault() method which can return a default value supplied by you - in case no value is stored in the Map by the given key. Here is an example of getting a value from a Java Map with a backup default value:
Map map = new HashMap();
map.put("A", "1");
map.put("B", "2");
map.put("C", "3");
Object value = map.getOrDefault("E", "default value");
This example creates a Map and stores three values in it using the keys A, B and C. Then the example calls the Map getOrDefault() method, passing the String E as key, along with a default value - the String default value. Since the Map does not contain any object stored by the key E the given default value will be returned - which is the String default value passed as the last parameter to the getOrDefault() method.
Checking if Map Contains Key
We can check if a Java Map contains a specific key using the containsKey() method. Here is how that looks:
boolean hasKey = mapA.containsKey("123");
After running this code, the hasKey variable will have the value true if a key + value pair was inserted earlier with the String key 123, and false if no such key + value pair was inserted.
Checking if Map Contains Value
The Java Map interface also has a method that enables you to check if the Map contains a certain value. The method is called containsValue() . Here is how calling the containsValue() looks:
boolean hasValue = mapA.containsValue("value 1");
After executing this code the hasValue variable will contain the value true if a key + value pair was inserted ealier with the String value "value 1", and false if not.
Iterating the Values of a Java Map
It is also possible to just iterate the values stored in a Java Map. You obtain a Set of the values stored in a Map via the values() method. You can iterate the values in the Set in following ways:
-
Using an Iterator
-
Using the for-each Loop
-
Using a value Stream
-
Using a Value Iterator
:
The first way to iterate all values stored in a Java Map is to obtain a value Iterator instance from the value Set, and iterate that. Here is how iterating the values stored in a Java Map using a value Iterator:
Iterator iterator = map.values().iterator();
while(iterator.hasNext()) {
Object nextValue iterator.next();
}
Since a Set is unordered, you do not have any guarantees about the order in which the values are iterated.
Using a Value For-Each Loop
:
The second method of iterating the values stores in a Java Map is via the Java for-each loop. Here is how iterating the values of a Java Map using the for-each loop looks in code:
for(Object value : mapA.values()){
System.out.println(value);
}
This example will print out all the values store in the mapA Map variable.
Using a Value Stream
:
The third way to iterate the values stored in a Java Map is by using a value Stream, by using the Java Stream API. You first obtain the value Set from the Map, and from the value Set you can obtain the Stream. Here is an example of iterating the values of a Java Map via a value Stream:
Map<String, String> map = new HashMap<>();
map.put("one", "first");
map.put("two", "second");
map.put("three", "third");
Stream<String> stream = map.values().stream();
stream.forEach((value) -> {
System.out.println(value);
});
Removing Entries From a Java Map
We remove Entries by calling the remove(Object key) method. We thus remove the (key, value) pair matching the key. Here is an example of removing the entry for a given key in a Java Map :
mapA.remove("key1");
After executing this instruction, the Map referenced by mapA will no longer contain an entry (key + value pair) for the key key1.
Removing All Entries
We can remove all entries in a Java Map using the clear() method. Here is how that looks:
mapA.clear();
Generic Java Maps
By default we can put any Object into a Map, but from Java 5, Java Generics makes it possible to limit the types of object you can use for both keys and values in a Map. Here is an example:
Map<String, MyObject> map = new HashMap<String, MyObject>();
This Map can now only accept String objects for keys, and MyObject instances for values. We can then access and iterate keys and values without casting them. Here is how it looks:
for(MyObject anObject : map.values()){
//do someting with anObject...
}
for(String key : map.keySet()){
MyObject value = map.get(key);
//do something to value
}
There are few methods in Java Map introduced in Java 8
:
default V getOrDefault(Object key, V defaultValue)
: Returns the value to which the specified key is mapped, or defaultValue if this map contains no mapping for the key.default void forEach(BiConsumer<? super K, ? super V> action)
: Performs the given action for each entry in this map.default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
: Replaces each entry’s value with the result of invoking the given function on that entry.default V putIfAbsent(K key, V value)
: If the specified key is not already associated with a value (or is mapped to null) associates it with the given value and returns null, else returns the current value.default boolean remove(Object key, Object value)
: Removes the entry for the specified key only if it is currently mapped to the specified value.default boolean replace(K key, V oldValue, V newValue)
: Replaces the entry for the specified key only if currently mapped to the specified value.default V replace(K key, V value)
: Replaces the entry for the specified key only if it is currently mapped to some value.- `default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction): If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
: If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value. If the function returns null, the mapping is removed.default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
: Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping).default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
: If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null.
Note : You will notice that all the new methods added in the Java 8 Map interface are default methods with implementation. This is done to make sure no compilation error occurs for any classes implementing Map interface.
Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.