- Details
- Written by Nam Ha Minh
- Last Updated on 14 June 2019   |   Print Email
This tutorial helps you understand and master Map - a member in the
Java Collections Framework. You will learn about:
A
Map is an object that maps keys to values, or is a collection of attribute-value pairs. It models the function abstraction in mathematics. The following picture illustrates a map:
Note that a
Map is not considered to be a true collection, as the
Map interface does not extend the
Collection interface. Instead, it starts an independent branch in the Java Collections Framework, as shown in the following diagram:
Characteristics of a Map:
Because a
Map is not a true collection, its characteristics and behaviors are different than the other collections like
List or
Set.A
Map cannot contain duplicate keys and each key can map to at most one value. Some implementations allow null key and null value (
HashMap and
LinkedHashMap) but some does not (
TreeMap).
The order of a map depends on specific implementations, e.g
TreeMap and
LinkedHashMap have predictable order, while
HashMap does not.
Maps are perfectly for key-value association mapping such as dictionaries. Use Maps when you want to retrieve and update elements by keys, or perform lookups by keys. Some examples:
- 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).
This tutorial provides code examples around the three major implementations of Map which are described below.
In the inheritance tree of the Map interface, there are several implementations but only 3 major, common, and general purpose implementations - they are
HashMap and
LinkedHashMap and
TreeMap. Let’s see the characteristics and behaviors of each implementation:
- HashMap: this implementation uses a hash table as the underlying data structure. It implements all of the Map operations and allows null values and one null key. This class is roughly equivalent to Hashtable - a legacy data structure before Java Collections Framework, but it is not synchronized and permits nulls. HashMap does not guarantee the order of its key-value elements. Therefore, consider to use a HashMap when order does not matter and nulls are acceptable.
- LinkedHashMap: this implementation uses a hash table and a linked list as the underlying data structures, thus the order of a LinkedHashMap is predictable, with insertion-order as the default order. This implementation also allows nulls like HashMap. So consider using a LinkedHashMap when you want a Map with its key-value pairs are sorted by their insertion order.
- TreeMap: this implementation uses a red-black tree as the underlying data structure. A TreeMap is sorted according to the natural ordering of its keys, or by a Comparator provided at creation time. This implementation does not allow nulls. So consider using a TreeMap when you want a Map sorts its key-value pairs by the natural order of the keys (e.g. alphabetic order or numeric order), or by a custom order you specify.
So far you have understood the key differences of the 3 major Map’s implementations. And the code examples in this tutorial are around them.Now, let’s see how to use Map for your daily coding.
Creating a HashMap:
Always use interface type (
Map), generics and diamond operator to declare a new map. The following code creates a
HashMap:
Map<Integer, String> mapHttpErrors = new HashMap<>();
mapHttpErrors.put(200, "OK");
mapHttpErrors.put(303, "See Other");
mapHttpErrors.put(404, "Not Found");
mapHttpErrors.put(500, "Internal Server Error");
System.out.println(mapHttpErrors);
This maps HTTP status codes to their descriptions. Output:
{404=Not Found, 500=Internal Server Error, 200=OK, 303=See Other}
As you can see in the output, a
HashMap does not impose any order on its key-value elements.You can create a new Map that copies elements from an existing map. For example:
Map<Integer, String> mapErrors = new HashMap<>(mapHttpErrors);
The map
mapErrors is created with initial elements copied from the map
mapHttpErrors.
Creating a LinkedHashMap:
The following code creates a
LinkedHashMap that maps phone numbers with contact names:
Map<String, String> mapContacts = new LinkedHashMap<>();
mapContacts.put("0169238175", "Tom");
mapContacts.put("0904891321", "Peter");
mapContacts.put("0945678912", "Mary");
mapContacts.put("0981127421", "John");
System.out.println(mapContacts);
Output:
{0169238175=Tom, 0904891321=Peter, 0945678912=Mary, 0981127421=John}
As you can see, the
LinkedHashMap maintains its elements by their insertion order.
Creating a TreeMap:
The following code creates a
TreeMap that maps file extensions to programming languages:
Map<String, String> mapLang = new TreeMap<>();
mapLang.put(".c", "C");
mapLang.put(".java", "Java");
mapLang.put(".pl", "Perl");
mapLang.put(".cs", "C#");
mapLang.put(".php", "PHP");
mapLang.put(".cpp", "C++");
mapLang.put(".xml", "XML");
System.out.println(mapLang);
Output:
{.c=C, .cpp=C++, .cs=C#, .java=Java, .php=PHP, .pl=Perl, .xml=XML}
As you can see, the
TreeMap sorts its keys by their
natural ordering, which is the alphabetical order in this case.
The basic operations of a Map are association (
put), lookup (
get), checking (
containsKeyand
containsValue), modification (
removeand
replace) and cardinality (
size and
isEmpty).
Associating a value with a key:
The
put(K, V) method associates the specified value V with the specified key K. If the map already contains a mapping for the key, the old value is replaced by the specified value:
Map<Integer, String> mapHttpErrors = new HashMap<>();
mapHttpErrors.put(400, "Bad Request");
mapHttpErrors.put(304, "Not Modified");
mapHttpErrors.put(200, "OK");
mapHttpErrors.put(301, "Moved Permanently");
mapHttpErrors.put(500, "Internal Server Error");
Getting a value associated with a specified key:
The
get(Object key) method returns the value associated with the specified key, or returns null if the map contains no mapping for the key. Given the map in the previous example:
String status301 = mapHttpErrors.get(301);
System.out.println("301: " + status301);
Output:
301: Moved Permanently
Checking if the map contains a specified key:
The method
containsKey(Object key) returns true if the map contains a mapping for the specified key. For example:
if (mapHttpErrors.containsKey("200")) {
System.out.println("Http status 200");
}
Output:
Found: Http status 200
Checking if the map contains a specified value:
The method
containsValue(Object value) returns true if the map contains one or more keys associated with the specified value. For example:
if (mapHttpErrors.containsValue("OK")) {
System.out.println("Found status OK");
}
Output:
Found status OK
Removing a mapping from the map:
The
remove(Object key) method removes the mapping for a key from the map if it is present (we care about only the key, and the value does not matter). This method returns the value to which the map previously associated the key, or null if the map doesn’t contain mapping for the key. Here’s an example:
String removedValue = mapHttpErrors.remove(500);
if (removedValue != null) {
System.out.println("Removed value: " + removedValue);
}
Output:
Removed value: Internal Server Error
Similarly, the
remove(Object key, Object value) method removes the mapping of a specified key and specified value, and returns true if the value was removed. This method is useful in case we really care about the key and value to be removed.I recommend you to read this
well-know Java collection book to learn in-depth about Java collections framework.
Replacing a value associated with a specified key:
The
replace(K key, V value)method replaces the entry for the specified key only if it is currently mapping to some value. This method returns the previous value associated with the specified key. Here’s an example:
System.out.println("Map before: " + mapHttpErrors);
mapHttpErrors.replace(304, "No Changes");
System.out.println("Map after: " + mapHttpErrors);
Output:
Map before: {400=Bad Request, 304=Not Modified, 200=OK, 301=Moved Permanently}
Map after: {400=Bad Request, 304=No Changes, 200=OK, 301=Moved Permanently}
Similarly, the
replace(K key, V oldValue, V newValue) method replaces the entry for the specified key only if it is currently mapping to the specified value. This method returns true if the value was replaced. Useful in case we want to replace exactly a key-value mapping.
Getting the size of the map:
The
size()method returns the number of key-value mappings in this map. For example:
int size = mapHttpErrors.size();
Output:
Number of HTTP status code: 5
Checking if the map is empty:
The
isEmpty() method returns true if the map contains no key-value mappings. For example:
if (mapHttpErrors.isEmpty()) {
System.out.println("No Error");
} else {
System.out.println("Have HTTP Errors");
}
Output:
Have HTTP Errors
As a Map is not a true collection, there is no direct method for iterating over a map. Instead, we can iterate over a map using its collection views. Any Map’s implementation has to provide the following three Collection view methods:
- keySet(): returns a Set view of the keys contained in the map. Hence we can iterate over the keys of the map as shown in the following example:
Map<String, String> mapCountryCodes = new HashMap<>();
mapCountryCodes.put("1", "USA");
mapCountryCodes.put("44", "United Kingdom");
mapCountryCodes.put("33", "France");
mapCountryCodes.put("81", "Japan");
Set<String> setCodes = mapCountryCodes.keySet();
Iterator<String> iterator = setCodes.iterator();
while (iterator.hasNext()) {
String code = iterator.next();
String country = mapCountryCodes.get(code);
System.out.println(code + " => " + country);
}
Output:44 => United Kingdom
33 => France
1 => USA
81 => Japan
- values(): returns a collection of values contained in the map. Thus we can iterate over values of the map like this:
Collection<String> countries = mapCountryCodes.values();
for (String country : countries) {
System.out.println(country);
}
Output:United Kingdom
France
USA
Japan
- entrySet(): returns a Set view of the mappings contained in this map. Therefore we can iterate over mappings in the map like this:
Set<Map.Entry<String, String>> entries = mapCountryCodes.entrySet();
for (Map.Entry<String, String> entry : entries) {
String code = entry.getKey();
String country = entry.getValue();
System.out.println(code + " => " + country);
}
Output:
44 => United Kingdom
33 => France
1 => USA
81 => Japan
Since Java 8 with Lambda expressions and the
forEach() statement, iterating over a Map is as easy as:
mapCountryCodes.forEach(
(code, country) -> System.out.println(code + " => " + country));
Output:
44 => United Kingdom
33 => France
1 => USA
81 => Japan
For more information about the different methods of collection iteration, read this article:
The 4 Methods for Iterating Collections in Java.
There are two bulk operations with maps:
clear() and
putAll().The
clear() method removes all mappings from the map. The map will be empty after this method returns. For example:
mapHttpErrors.clear();
System.out.println("Is map empty? " + mapHttpErrors.isEmpty());
Output:
Is map empty? true
The
putAll(Map<K, V> m) method copies all of the mappings from the specified map to this map. Here’s an example:
Map<Integer, String> countryCodesEU = new HashMap<>();
countryCodesEU.put(44, "United Kingdom");
countryCodesEU.put(33, "France");
countryCodesEU.put(49, "Germany");
Map<Integer, String> countryCodesWorld = new HashMap<>();
countryCodesWorld.put(1, "United States");
countryCodesWorld.put(86, "China");
countryCodesWorld.put(82, "South Korea");
System.out.println("Before: " + countryCodesWorld);
countryCodesWorld.putAll(countryCodesEU);
System.out.println("After: " + countryCodesWorld);
Output:
Before: {1=United States, 82=South Korea, 86=China}
After: {1=United States, 33=France, 49=Germany, 82=South Korea, 86=China, 44=United Kingdom}
Unlike the legacy
Hashtable which is synchronized, the
HashMap,
TreeMap and
LinkedHashMap are not synchronized. If thread-safe is priority, consider using
ConcurrentHashMap in place of
HashMap. Or we can use the
Collections.synchronizedMap() utility method that returns a synchronized (thread-safe) map backed by the specified map. For example:
Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());
And remember we have to manually synchronize the map when iterating over any of its collection views:
Set<Integer> keySet = map.keySet();
synchronized (map) {
Iterator<Integer> iterator = keySet.iterator();
while (iterator.hasNext()) {
Integer key = iterator.next();
String value = map.get(key);
}
}
If you use a kind of
SortedMap, e.g.
TreeMap, consider using the more specific method
Collections.synchronizedSortedMap().
NOTE: If you use your own type for the key and value (e.g.
Student or
Employee), the key class and value class must implement the
equals() and
hashCode() methods properly so that the map can look up them correctly.
API References
Related Map Tutorials:
Other Java Collections Tutorials:
That's a comprehensive and great detailed tutorial about Java map. I hope you grasp something new and enjoy learning. If you have time and budget, I recommend you to take this
Java masterclass course to learn Java programming in-depth.
About the Author:
Nam Ha Minh is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on
Facebook and watch
his Java videos you YouTube.