Java HashMap is a hash table based implementation of Java’s Map interface. A Map, as you might know, is a collection of key-value pairs. It maps keys to values.
Following are few key points to note about HashMaps in Java -
A HashMap cannot contain duplicate keys.
Java HashMap allows
null
values and thenull
key.HashMap is an unordered collection. It does not guarantee any specific order of the elements.
Java HashMap is not thread-safe. You must explicitly synchronize concurrent modifications to the HashMap.
Creating a HashMap and Adding key-value pairs to it
The following example shows how to create a HashMap, and add new key-value pairs to it.
import java.util.HashMap;
import java.util.Map;
public class CreateHashMapExample {
public static void main(String[] args) {
// Creating a HashMap
Map<String, Integer> numberMapping = new HashMap<>();
// Adding key-value pairs to a HashMap
numberMapping.put("One", 1);
numberMapping.put("Two", 2);
numberMapping.put("Three", 3);
// Add a new key-value pair only if the key does not exist in the HashMap, or is mapped to `null`
numberMapping.putIfAbsent("Four", 4);
System.out.println(numberMapping);
}
}
# Output
{One=1, Four=4, Two=2, Three=3}
Accessing keys and modifying their associated value in a HashMap
The example below shows:
- How to check if a HashMap is empty |
isEmpty()
- How to find the size of a HashMap |
size()
- How to check if a given key exists in a HashMap |
containsKey()
- How to check if a given value exists in a HashMap |
containsValue()
- How to get the value associated with a given key in the HashMap |
get()
- How to modify the value associated with a given key in the HashMap |
put()
import java.util.HashMap;
import java.util.Map;
public class AccessKeysFromHashMapExample {
public static void main(String[] args) {
Map<String, String> userCityMapping = new HashMap<>();
// Check if a HashMap is empty
System.out.println("is userCityMapping empty? : " + userCityMapping.isEmpty());
userCityMapping.put("John", "New York");
userCityMapping.put("Rajeev", "Bengaluru");
userCityMapping.put("Steve", "London");
System.out.println("userCityMapping HashMap : " + userCityMapping);
// Find the size of a HashMap
System.out.println("We have the city information of " + userCityMapping.size() + " users");
String userName = "Steve";
// Check if a key exists in the HashMap
if(userCityMapping.containsKey(userName)) {
// Get the value assigned to a given key in the HashMap
String city = userCityMapping.get(userName);
System.out.println(userName + " lives in " + city);
} else {
System.out.println("City details not found for user " + userName);
}
// Check if a value exists in a HashMap
if(userCityMapping.containsValue("New York")) {
System.out.println("There is a user in the userCityMapping who lives in New York");
} else {
System.out.println("There is no user in the userCityMapping who lives in New York");
}
// Modify the value assigned to an existing key
userCityMapping.put(userName, "California");
System.out.println(userName + " moved to a new city " + userCityMapping.get(userName) + ", New userCityMapping : " + userCityMapping);
// The get() method returns `null` if the specified key was not found in the HashMap
System.out.println("Lisa's city : " + userCityMapping.get("Lisa"));
}
}
# Output
is userCityMapping empty? : true
userCityMapping HashMap : {Steve=London, John=New York, Rajeev=Bengaluru}
We have the city information of 3 users
Steve lives in London
There is a user in the userCityMapping who lives in New York
Steve moved to a new city California, New userCityMapping : {Steve=California, John=New York, Rajeev=Bengaluru}
Lisa's city : null
Removing keys from a HashMap
The following example shows how to :
- Remove a key from a HashMap | remove(Object key)
- Remove a key from a HashMap only if it is associated with a given value | remove(Object key, Object value)
import java.util.HashMap;
import java.util.Map;
public class RemoveKeysFromHashMapExample {
public static void main(String[] args) {
Map<String, String> husbandWifeMapping = new HashMap<>();
husbandWifeMapping.put("Jack", "Marie");
husbandWifeMapping.put("Chris", "Lisa");
husbandWifeMapping.put("Steve", "Jennifer");
System.out.println("Husband-Wife Mapping : " + husbandWifeMapping);
// Remove a key from the HashMap
// Ex - Unfortunately, Chris got divorced. Let's remove him from the mapping
String husband = "Chris";
String wife = husbandWifeMapping.remove(husband);
System.out.println("Couple (" + husband + " => " + wife + ") got divorced");
System.out.println("New Mapping : " + husbandWifeMapping);
// Remove a key from the HashMap only if it is mapped to the given value
// Ex - Divorce "Jack" only if He is married to "Linda"
boolean isRemoved = husbandWifeMapping.remove("Jack", "Linda");
System.out.println("Did Jack get removed from the mapping? : " + isRemoved);
// remove() returns null if the mapping was not found for the supplied key
wife = husbandWifeMapping.remove("David");
if(wife == null) {
System.out.println("Looks like David is not married to anyone");
} else {
System.out.println("Removed David and his wife from the mapping");
}
}
}
# Output
Husband-Wife Mapping : {Steve=Jennifer, Chris=Lisa, Jack=Marie}
Couple (Chris => Lisa) got divorced
New Mapping : {Steve=Jennifer, Jack=Marie}
Did Jack get removed from the mapping? : false
Looks like David is not married to anyone
Obtaining the entrySet, keySet, and values from a HashMap
The Map
interface provides methods to retrieve the set of entries (key-value pairs), the set of keys, and the collection of values.
The following example shows how to retrieve them from a HashMap -
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapEntryKeySetValuesExample {
public static void main(String[] args) {
Map<String, String> countryISOCodeMapping = new HashMap<>();
countryISOCodeMapping.put("India", "IN");
countryISOCodeMapping.put("United States of America", "US");
countryISOCodeMapping.put("Russia", "RU");
countryISOCodeMapping.put("Japan", "JP");
countryISOCodeMapping.put("China", "CN");
// HashMap's entry set
Set<Map.Entry<String, String>> countryISOCodeEntries = countryISOCodeMapping.entrySet();
System.out.println("countryISOCode entries : " + countryISOCodeEntries);
// HashMap's key set
Set<String> countries = countryISOCodeMapping.keySet();
System.out.println("countries : " + countries);
// HashMap's values
Collection<String> isoCodes = countryISOCodeMapping.values();
System.out.println("isoCodes : " + isoCodes);
}
}
# Output
countryISOCode entries : [United States of America=US, Japan=JP, China=CN, India=IN, Russia=RU]
countries : [United States of America, Japan, China, India, Russia]
isoCodes : [US, JP, CN, IN, RU]
Iterating over a HashMap
The following example shows different ways of iterating over a HashMap -
Iterating over a HashMap using Java 8 forEach and lambda expression.
Iterating over the HashMap’s entrySet using iterator().
Iterating over the HashMap’s entrySet using Java 8 forEach and lambda expression.
Iterating over the HashMap’s entrySet using simple for-each loop.
Iterating over the HashMap’s keySet.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class IterateOverHashMap {
public static void main(String[] args) {
Map<String, Double> employeeSalary = new HashMap<>();
employeeSalary.put("David", 76000.00);
employeeSalary.put("John", 120000.00);
employeeSalary.put("Mark", 95000.00);
employeeSalary.put("Steven", 134000.00);
System.out.println("=== Iterating over a HashMap using Java 8 forEach and lambda ===");
employeeSalary.forEach((employee, salary) -> {
System.out.println(employee + " => " + salary);
});
System.out.println("\n=== Iterating over the HashMap's entrySet using iterator() ===");
Set<Map.Entry<String, Double>> employeeSalaryEntries = employeeSalary.entrySet();
Iterator<Map.Entry<String, Double>> employeeSalaryIterator = employeeSalaryEntries.iterator();
while (employeeSalaryIterator.hasNext()) {
Map.Entry<String, Double> entry = employeeSalaryIterator.next();
System.out.println(entry.getKey() + " => " + entry.getValue());
}
System.out.println("\n=== Iterating over the HashMap's entrySet using Java 8 forEach and lambda ===");
employeeSalary.entrySet().forEach(entry -> {
System.out.println(entry.getKey() + " => " + entry.getValue());
});
System.out.println("\n=== Iterating over the HashMap's entrySet using simple for-each loop ===");
for(Map.Entry<String, Double> entry: employeeSalary.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
System.out.println("\n=== Iterating over the HashMap's keySet ===");
employeeSalary.keySet().forEach(employee -> {
System.out.println(employee + " => " + employeeSalary.get(employee));
});
}
}
# Output
=== Iterating over a HashMap using Java 8 forEach and lambda ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0
=== Iterating over the HashMap's entrySet using iterator() ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0
=== Iterating over the HashMap's entrySet using Java 8 forEach and lambda ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0
=== Iterating over the HashMap's entrySet using simple for-each loop ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0
=== Iterating over the HashMap's keySet ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0
Java HashMap with User defined objects
Check out the following example to learn how to create and work with a HashMap of user defined objects.
import java.util.HashMap;
import java.util.Map;
class Employee {
private Integer id;
private String name;
private String city;
public Employee(Integer id, String name, String city) {
this.id = id;
this.name = name;
this.city = city;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", city='" + city + '\'' +
'}';
}
}
public class HashMapUserDefinedObjectExample {
public static void main(String[] args) {
Map<Integer, Employee> employeesMap = new HashMap<>();
employeesMap.put(1001, new Employee(1001, "Rajeev", "Bengaluru"));
employeesMap.put(1002, new Employee(1002, "David", "New York"));
employeesMap.put(1003, new Employee(1003, "Jack", "Paris"));
System.out.println(employeesMap);
}
}
# Output
{1001=Employee{name='Rajeev', city='Bengaluru'}, 1002=Employee{name='David', city='New York'}, 1003=Employee{name='Jack', city='Paris'}}
Synchronizing Access to Java HashMap
Java HashMap is not thread-safe. It may become non-deterministic in multi-threaded environments where multiple threads try to modify the HashMap concurrently.
Example demonstrating HashMap’s unpredictable behavior in multi-threaded environments
The following example demonstrates how HashMap becomes non-deterministic when multiple threads try to modify it at the same time -
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class UnsafeHashMapExample {
public static void main(String[] args) throws InterruptedException {
Map<String, Integer> cricketTeamScore = new HashMap<>();
cricketTeamScore.put("Australia", 349);
cricketTeamScore.put("India", 250);
// Create an ExecutorService with a Thread Pool of size 10
ExecutorService executorService = Executors.newFixedThreadPool(10);
// Create a Runnable object that increments the value associated with a given key in the HashMap by one.
Runnable task = () -> {
incrementTeamScore(cricketTeamScore, "India");
};
// Submit the Runnable object to the executorService 100 times to test concurrent modifications
for(int i = 0; i < 100; i++) {
executorService.submit(task);
}
executorService.shutdown();
executorService.awaitTermination(60, TimeUnit.SECONDS);
System.out.println("Final Score of Team India : " + cricketTeamScore.get("India"));
}
// Increment the score of a team by one
private static void incrementTeamScore(Map<String, Integer> cricketTeamScore, String team) {
Integer score = cricketTeamScore.get(team);
cricketTeamScore.put(team, score + 1);
}
}
The final output of the above program should be 350
because the initial value was 250
, and we’re incrementing it 100 times.
But since multiple threads try to modify the HashMap concurrently, the change done by one thread gets overridden by some other thread, and the output becomes non-deterministic.
If you run the above program multiple times, you’ll find that it produces different output each time it is run.
# Output
Final Score of Team India : 343
You can learn more about concurrency issues like this from my Java Concurrency Issues and Thread Synchronization tutorial.
Example demonstrating how to synchronize concurrent modifications to a HashMap
Let’s write the thread-safe version of the previous program. We can make the following two changes to the program to make it thread-safe -
Use the
Collections.synchronizedMap()
method to obtain a synchronized view of the HashMap.Write the increment logic inside a
synchronized
block.
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
public class SynchronizedHashMapExample {
public static void main(String[] args) throws InterruptedException {
Map<String, Integer> cricketTeamScore = Collections.synchronizedMap(new HashMap<>());
cricketTeamScore.put("Australia", 349);
cricketTeamScore.put("India", 250);
// Create an ExecutorService with a Thread Pool of size 10
ExecutorService executorService = Executors.newFixedThreadPool(10);
// Create a Runnable object that increments the value associated with a given key in the HashMap by one.
Runnable task = () -> {
incrementTeamScore(cricketTeamScore, "India");
};
// Submit the Runnable object to the executorService 100 times to test concurrent modifications
for(int i = 0; i < 100; i++) {
executorService.submit(task);
}
executorService.shutdown();
executorService.awaitTermination(60, TimeUnit.SECONDS);
System.out.println("Final Score of Team India : " + cricketTeamScore.get("India"));
}
// Increment the score of a team by one
private static void incrementTeamScore(Map<String, Integer> cricketTeamScore, String team) {
synchronized (cricketTeamScore) {
Integer score = cricketTeamScore.get(team);
cricketTeamScore.put(team, score + 1);
}
}
}
This program produces the correct output -
# Output
Final Score of Team India : 350
You can also use a ConcurrentHashMap for thread safety instead of the HashMap obtained via Collections.synchronizedMap()
method. The ConcurrentHashMap provides thread-safe operations on the Map.
Conclusion
Congratulations folks! In this article, you learned what is a HashMap, how to create a HashMap, how to add new key-value pairs to a HashMap, how to remove keys from a HashMap, how to iterate over a HashMap, and how to synchronize a HashMap.
As always, Thanks for reading. See you in the next post.