Java Maps- My toolbox

Arvind Telharkar
Effective Java
Published in
6 min readDec 13, 2021

The Map interface in Java is one of the most powerful structures used in business logic implementation. For new programmers, it is crucial to have a good command over the Map interface and its various implementations. Let’s dive into some of the most important tools and key points while dealing with maps in Java — I call it “My Maps Toolbox”!

Photo by Hal Gatewood on Unsplash

1. Implementations

Since Map is an interface, you need to be aware of the classes that implement it. For all practical purposes, the most important implementations are —

  • HashMap — This is just a simple key-value store. There is no control over the ordering of keys. For more, the Javadoc for HashMap can be found here.
  • TreeMap — If you want all the qualities of a HashMap and in addition if you want your keys to be sorted in some order, TreeMap is the way to go. For more, the Javadoc for TreeMap can be found here
  • LinkedHashMap — If you want the keys in the map to preserve the order of insertion(which is not preserved in a normal HashMap), LinkedHashMap is the way to go. For more, the Javadoc for LinkedHashMap can be found here.
  • Hashtable — Quite similar to HashMap overall. Hashtable is synchronized and thread-safe. If thread safety is not something you care about, use of a HashMap is preferable. For more, the Javadoc for Hashtable can be found here.

2. Insertions and Retrievals

For any Map, the put method is used to insert a key-value pair, and the get method is used to retrieve a value for a given key, if it exists. These are the very basic operations while working with any Map.

  • put(key, value) — This method will insert the key-value pair in the Map. If the same key already exists, the value will be overwritten after calling put. The Javadoc for put can be found here.
  • get(key) — This method will fetch the value for a given key, if it exists in the Map, else null. The Javadoc for get can be found here.
  • containsKey(key) — This is an extremely important method to know while working with put and get. This will return true if a key exists in the map. The Javadoc for containsKey can be found here.

To sum up insertions and retrievals, let’s take an example where we are looking at a list of names and we want to find the frequency(how many times a name appeared) for each name. You could use a HashMap with the name as a String key and the value as an Integer.

// Note how the naming of the map indicates keys and values
Map<String, Integer> namesToFrequencies = new HashMap<String, Integer>();
// Let's assume nameList contains the list of names- loop over it
for(String name: nameList) {
/*
If a name key is not contained in the map, its value is 0
If it does exist, we simply fetch the existing value with get()
*/
int currentNameFrequency = namesToFrequencies.containsKey(name) ?
namesToFrequencies.get(name) : 0;
// We update the count by 1, since we have the current occurrence
int updatedNameFrequency = currentNameFrequency + 1;
/*
Finally, we put the updated count value in the map for name
Note how this takes care of new names which would have frequency set as 1 as well existing names getting their frequency incremented by 1
*/
namesToFrequencies.put(name, updatedNameFrequency);}

Once the loop finishes, the map should have all the distinct names with their frequencies stored in it. This is a fundamental technique — and a MUST MASTER, if you want to have any success with maps in Java.

3. Iteration

Iteration can involve iteration over all keys, all values or just iterating over each key-value pair altogether.

  • keySet() — This method will give a Set of all keys(unique keys!) in the map. For more, the Javadoc for keySet can be found here.
  • values() — This method will give a collection of all values(only values, not keys) in the map. For more, the Javadoc for values can be found here.
  • entrySet() — This method will give you a view of all the mappings in the map, both keys and values. For more, the Javadoc for entrySet can be found here.

Let’s see this in an example —

Map<String, Integer> namesToFrequencies = new HashMap<String, Integer>();namesToFrequencies.put("David", 1);
namesToFrequencies.put("Robert", 3);
namesToFrequencies.put("John", 5);
// This would print ["David", "Robert", "John"] in some order
System.out.println(namesToFrequencies.keySet());
// This would print [1, 3 ,5] in some order
System.out.println(namesToFrequencies.values());
// This would print ["David"=1, "Robert"=3, "John"=5] in some order
System.out.println(namesToFrequencies.entrySet());

4. Sorting based on a parameter

This is really important for interviewing with Big Tech. You need to be really comfortable with the basics of how a map works, the basic methods discussed above, iteration etc. to get into this. This is where many people mess up in interviews — after correctly realizing that they need to use a HashMap to store data, most people can’t make the cut due to the lack of a clear strategy when it comes to this part.

2 important steps —

  • Step 1: Implement a custom Comparator

If you don’t know what a Comparator is — you can read about it here. This is not the Javadoc, but a useful link to get a quick idea. The Javadoc for Comparator interface can be found here.

In simple words, a comparator is a mechanism of defining custom logic for comparing two objects of a type. For e.g. A custom comparator for 2 Points would compare 2 point objects Point A and Point B based on their distance from the origin. This would decide how points would be ordered, if we were to sort a list of Point objects.

  • Step 2: Using the comparator, either use a TreeMap to sort the keys according to the custom sorting logic in the comparator or just use Collections.sort(), passing it the comparator.

Let us take an example where we have a list of coffeeShops and we want to find the 10 closest ones from our location. Let us assume that the calculation for the distances for each of the coffeeshops has been done and stored in a HashMap called coffeeShopsToDistances. Let us assume that that the CoffeeShop class is implemented already.

Step 1: Implement a custom comparator for comparing CoffeeShop objectspublic class CoffeeShopComparator implements Comparator<CoffeeShop> {
/*
We define our custom logic for comparing 2 CoffeeShop objects here. For that we do a get() on the coffeeShopsToDistances HashMap to get the distance values.
*/
@Override
public int compare(CoffeeShop a, CoffeeShop b) {
int distanceA = coffeeShopsToDistances.get(a);
int distanceB = coffeeShopsToDistances.get(b);
if(distanceA > distanceB) {
return 1;
} else if(distanceA < distanceB) {
return -1;
} else {
return 0; // You can just implement a.compareTo(b) as well
}
}
}

Now that we have the comparator for CoffeeShop objects, we can take a look at the keys in the HashMap.

 Step 2: We will use Collections.sort() using the comparator above. Some people will argue that using a TreeMap is easier here, but I think this approach is applicable to a wider range of problems and is more extensible. Using a TreeMap achieves the same. However, it hides what it is actually happening under the hood- which is extremely useful to understand while going over this example.//Get the keys in the HashMap with the keySet() method.
List<CoffeeShop> coffeeShops = new ArrayList<CoffeeShop>(coffeeShopsToDistances.keySet());
/*
Sort this list of coffeeshops based on criteria defined in our custom comparator described in the above step.
*/
Collections.sort(coffeeShops, new CoffeeShopComparator());/*
We now have the coffeeshop list sorted according to the distance from us. All we need to do is pick the first 10
*/
List<CoffeeShop> closestCoffeeShops = new ArrayList<CoffeeShop>();/*
Naive implementation assuming there are at least 10 coffeeshops!!
This can be made more robust and is only for demonstration purposes!

*/
for(int i=0;i<10;i++) {
closestCoffeeShops.add(coffeeShops.get(i));
}
closestCoffeeShops would now have our 10 closest coffee shops!!

If you want to connect with me, feel free to reach out on LinkedIn!

To take your Java development skills to the next level, be sure to checkout and follow Effective Java!

--

--