Essential Map Interface Methods for Java Developers
Discover key methods for using Java Maps effectively.
Hey there!
In this blog, we will explore must-know essential Java Map
interface methods that simplify the life of every Java coder!
I hope you are already familiar with Maps in Java. In simple terms, a Map is a collection that stores key-value pairs, allowing efficient retrieval, insertion, updation, and deletion of values based on their keys.
Map<String, Integer> personWithAmount = new HashMap<>(); // Creating a map to store name (string) as key and amount (int) as value.
personWithAmount.put("Rebecca", 120);
personWithAmount.put("James", 100);
personWithAmount.put("Alex", 100);
System.out.println(personWithAmount);
// OUTPUT
// {James=100, Alex=100, Rebecca=120}
In this blog, I will mainly talk about the below methods.
getOrDefault()
computeIfAbsent()
computeIfPresent()
putIfAbsent()
Other Highly Used Methods
Java Map
interface provides multiple methods that allow you to work efficiently. Some of the most commonly used methods include :
put(K key, V value)
- Inserts value to the specified key.get(K key)
- Retrieves the value associated with the specified key.remove(Object key)
- Remove the entry for the specified key.containsKey(Object key)
- Checks if an entry with the key is present or not.keySet()
- Returns a set view of the keys contained in the map.values()
- Returns a collection of values contained in the map.
There are many more. You can check this resource for a complete list of methods. These methods help us to manage and manipulate key-value pairs.
Play with Maps!
As mentioned above at the beginning of the blog, I will focus on those four methods. I found these methods really helpful for writing modular code. I will explain these methods with examples and show how and when we can use them.
✨ getOrDefault
The getOrDefault()
method returns a value for the specified key or a default value if the value is not found.
Now, let's say we want to store a list of names for every amount as a key. Here is what we would do.
class Solve {
public static void main(String[] args) {
Map<Integer, List<String>> amountWithNames = new HashMap<>();
List<Pair> people = new ArrayList<>(); // `people` is a list of pairs : Name(String) and Amount(Integer)
people.add(new Pair("Rebecca", 120));
people.add(new Pair("James", 100));
people.add(new Pair("Alex", 100));
for(int index = 0; index < people.size(); index++){
int amount = people.get(index).amount;
String name = people.get(index).name;
if(!amountWithNames.containsKey(amount)){ // If map doesn't contain the `amount` key, add a new list to it.
amountWithNames.put(amount, new ArrayList<>());
}
amountWithNames.get(amount).add(name); // add name to the corresponding amount
}
System.out.println(amountWithNames);
}
}
class Pair{
String name;
int amount;
Pair(String name, int amount){
this.name = name;
this.amount = amount;
}
}
// OUTPUT
// {100=[James, Alex], 120=[Rebecca]}
You can see there is code added to handle the case where the list is not present for a specific key. Here, we can make use of the getOrDefault
method. Let's see how.
.
.
for(int index = 0; index < people.size(); index++){
int amount = people.get(index).amount;
String name = people.get(index).name;
// Get the list of names associated with an amount, or create a new list if it doesn't exist
List<String> names = amountWithNames.getOrDefault(amount, new ArrayList<>());
names.add(name); // Add new name to the list
amountWithNames.put(amount, names); // Put this list to amount key
}
.
.
// OUTPUT
// {100=[James, Alex], 120=[Rebecca]}
You might wonder why we can't do something like this:
.
.
for(int index = 0; index < people.size(); index++){
int amount = people.get(index).amount;
String name = people.get(index).name;
amountWithNames.getOrDefault(amount, new ArrayList<>()).add(name);
}
.
.
// OUTPUT
// {}
Here, we are retrieving a new list to the key if it doesn't exist. Since getOrDefault
will return the entry (which is a list here) for the specified key, we will add the name to the returned list. Isn't that correct?
No, this is incorrect. Let me explain why with the following points.
When
getOrDefault
returns the default value (a newArrayList<>()
), this list is not stored in the map. It's just a temporary list returned for use.We added the
name
to this temporary list, but since this list isn't stored in the map, the map itself remains unchanged.The key is not added to the map because the
put
operation is missing. Since the temporary list is not associated with any key in the map, the map remains empty.
✨ computeIfAbsent
As the name suggests, computeIfAbsent(Key, Function)
method is used to compute a value for a specified key using the given mapping function and add it to the map if the key is not associated with any value (or is mapped to null). It returns the current (existing or computed) value associated with the specified key.
The mapping function takes the key as the argument and it should not have more than one argument.
Map<String, List<String>> amountWithNames = new HashMap<>();
String name = "Rebecca";
String amount = "100";
// If the key "100" is absent, this adds it with a new ArrayList, then adds "Rebecca" to that list.
amountWithNames.computeIfAbsent(amount, k -> new ArrayList<>()).add(name);
System.out.println(amountWithNames);
// OUTPUT
// {100=[Rebecca]}
In the above code, computeIfAbsent
creates and returns a new list associated with the specified key as it doesn't exist, and the name
is added to the returned list.
✨ computeIfPresent
As you might have guessed, the computeIfPresent(Key, Function)
method computes a new value for an entry based on its key and current value, but only if the key already exists. Similar to computeIfAbsent()
, it returns the value (in this case, the computed value) associated with the specified key.
In this method, the mapping function takes the key as the first argument and the value as the second.
Map<String, List<String>> amountWithNames = new HashMap<>();
String name = "Alex";
String amount = "100";
// If the key "100" is absent, this adds it with a new ArrayList, then adds "Rebecca" to that list.
amountWithNames.computeIfAbsent(amount, k -> new ArrayList<>()).add(name);
amountWithNames.computeIfPresent(amount, (k,v) -> {
v.add("Alex");
return v; // returns the updated list
});
System.out.println(amountWithNames);
// OUTPUT
// {100=[Rebecca, Alex]}
✨ putIfAbsent
The putIfAbsent(Key, Value)
method adds a new entry to a map only if an entry with the specified key doesn't exist. If an entry with the specified key exists, it returns the current value of that entry, otherwise, it returns null.
Map<String, List<String>> amountWithNames = new HashMap<>();
String name = "Rebecca";
String amount = "100";
amountWithNames.putIfAbsent(amount, new ArrayList<>()); // add a new list if an entry doesn't exists
amountWithNames.get(amount).add(name);
System.out.println(amountWithNames);
// OUTPUT
// {100=[Rebecca]}
How I Discovered These Methods
I already knew the getOrDefault
method and have been using it for quite a while now, but I discovered the others while solving a Data Structure and Algorithm (DSA) problem.
My solution involved using a list of maps, where the key is a string and the value is a list. I don't remember the exact question now, but I will try to provide some examples. My goal is to show you how and when to use these functions.
Let's briefly look at a few examples below with a problem statement.
Categorize students' grades in three different subjects: Math, Science, and English. The output will show which students achieved which grade in each subject.
// 5 students data with studentID, MathGrade, ScienceGrade, EnglishGrade
String[][] students = {
{"101", "A", "B", "A"},
{"102", "B", "A", "C"},
{"103", "A", "C", "B"},
{"104", "C", "B", "A"},
{"105", "B", "A", "B"}
};
// Create a list containing 3 HashMaps for Math, Science, and English
ArrayList<Map<String, ArrayList<String>>> listOfMap = new ArrayList<>();
// Map 0 is for Math, 1 is for Science, 2 is for English
for (int index = 0; index < 3; index++) {
listOfMap.add(new HashMap<>());
}
// Process each student's grades
for (String[] student : students) {
String studentID = student[0];
String mathGrade = student[1];
String scienceGrade = student[2];
String englishGrade = student[3];
if (!listOfMap.get(0).containsKey(mathGrade)) {
listOfMap.get(0).put(mathGrade, new ArrayList<>());
}
if (!listOfMap.get(1).containsKey(scienceGrade)) {
listOfMap.get(1).put(scienceGrade, new ArrayList<>());
}
if (!listOfMap.get(2).containsKey(englishGrade)) {
listOfMap.get(2).put(englishGrade, new ArrayList<>());
}
listOfMap.get(0).get(mathGrade).add(studentID);
listOfMap.get(1).get(scienceGrade).add(studentID);
listOfMap.get(2).get(englishGrade).add(studentID);
}
String[] subjects = {"Math", "Science", "English"};
for (int index = 0; index < 3; index++) {
System.out.println(subjects[index] + " Grades: " + listOfMap.get(index));
}
//OUTPUT
// Math Grades: {A=[101, 103], B=[102, 105], C=[104]}
// Science Grades: {A=[102, 105], B=[101, 104], C=[103]}
// English Grades: {A=[101, 104], B=[103, 105], C=[102]}
When using computeIfAbsent
, it makes the code modular. It combines the key check and insertion into a single method call, making the code more concise.
for (String[] student : students) {
String studentID = student[0];
String mathGrade = student[1];
String scienceGrade = student[2];
String englishGrade = student[3];
listOfMap.get(0).computeIfAbsent(mathGrade, k -> new ArrayList<>()).add(studentID);
listOfMap.get(1).computeIfAbsent(scienceGrade, k -> new ArrayList<>()).add(studentID);
listOfMap.get(2).computeIfAbsent(englishGrade, k -> new ArrayList<>()).add(studentID);
}
Same with using putIfAbsent()
. Let's briefly look at the code snippet.
for (String[] student : students) {
String studentID = student[0];
String mathGrade = student[1];
String scienceGrade = student[2];
String englishGrade = student[3];
listOfMap.get(0).putIfAbsent(mathGrade, new ArrayList<>());
listOfMap.get(0).get(mathGrade).add(studentID);
listOfMap.get(1).putIfAbsent(scienceGrade, new ArrayList<>());
listOfMap.get(1).get(scienceGrade).add(studentID);
listOfMap.get(2).putIfAbsent(englishGrade, new ArrayList<>());
listOfMap.get(2).get(englishGrade).add(studentID);
}
Not including the other two methods as the blog length would become too lengthy. I want to keep it precise and simple.
If you enjoyed this post, please consider hitting the clap button 👏 below or leaving a comment 💬 or both 🤗. Feel free to subscribe! This will encourage me to write more short, simple, and easy-to-understand blogs!
🍀Happy Learning!🍀