Java Map Enhancements

Java 8 introduced several default and utilities methods in the Map Interface, the purpose of these new methods/enhancements is to help engineers in writing more succinct code and follow conversational pattern. This article explains the new enhancements with meaningful examples and details. 

Sorting

Sorting has two new functions which helps us in sorting entries of Map by Keys and Values. 

  • Entry.comparingByKey
  • Entry.comparingByValue 

Consider a HashMap of Employees where Key is String (name of the Employee) and Value is Integer (age). 

Map<String, Integer> employees =
                    Map.ofEntries(entry("David", 40),
                                                     entry("Sameer", 36),
                                                     entry("Mick", 51),
                                                     entry("Trent", 55));

Since HashMap doesn't following ordering when we retrieve the Entries from the HashMap it returns them in random order. To Sort the Employees Map by key which is name (sort in natural order) 'comparingByKey' method which is a new addition in the Entry Interface should be used. 

//Sort By Key
employees
           .entrySet()
           .stream()
           .sorted(Map.Entry.comparingByKey())
           .forEach((e) -> System.out.println(e.getKey() + " - "+ e.getValue()));

David - 40
Mick - 51
Sameer - 36
Trent – 55

The above code prints the data in ascending order of names. To sort the employee data by age 'comparingByValue' method should be used. 

//Sort By Value
employees
           .entrySet()
           .stream()
           .sorted(Map.Entry.comparingByValue())
           .forEach((e) -> System.out.println(e.getKey() + " - "+ e.getValue()));

Sameer - 36
David - 40
Mick - 51
Trent – 55

It sorts the entries in ascending order of age. 

getOrDefault

When the key we are looking for isn't present in the Map it returns "null", now if the key isn't present it can return a default value using "getOrDefault" method. 

employees.get("Test"); // Returns null

employees.getOrDefault("Test", Integer.MIN_VALUE); // Returns -2147483648 value. 

Please note: No matter whether Key is present or not, if we use 'getOrDefault' the fallback will always be evaluated because that's how the implementation is. 

default V getOrDefault(Object key, V defaultValue) {
      V v;
             return (((v = get(key)) != null) || containsKey(key))
            ? v
           : defaultValue;
}

remove

Old "remove" method is used to remove the entry based on the Key passed to the "remove" function. To effectively use the existing remove function, we must write something like, 

String key = "Test";
if(employees.containsKey(key)){
        employees.remove(key);
        return true;
}
else {
     return false;
}

Java 8 has a new overloaded version of "remove" method which removes the entry when the key is associated with the correct value is supplied. 

employees.remove("David", 40) // true 
employees.remove("David", 10) // false
employees.remove("Test", 11) // false

If the Key is passed with the wrong value or the Key is not present, in both cases "remove" returns false 

Compute Methods

Java 8 compute methods helps in performing operation conditionally, there are 3 new operations for it.

  • computeIfAbsent: This operation computes the new value and associates with the key, As the name suggests, this operation computes the value of the key only when the key is absent, if the key is present then it doesn't compute the value. This Operation returns the value. 
    int v = employees.computeIfAbsent("Casey", key -> 67);
    
    System.out.println(v); // 67
    System.out.println(employees); // {Mick=51, Casey=67, David=40, Trent=55, Sameer=36}

    In the example, the key is not present in the Map, hence the operation computes the value and creates a new Entry. 

    int v = employees.computeIfAbsent("David", key -> 33);
    
    System.out.println(v); // 40
    System.out.println(employees); // {Mick=51, Casey=67, David=40, Trent=55, Sameer=36}

    Since the mapping already exists, it returns the existing value and does not compute the new value for the corresponding key.

  • computeIfPresent: This operation computes the new value when then the key is present in the Map. 

    int v = employees.computeIfAbsent("David", key -> 33);
    
    System.out.println(v); // 33
    System.out.println(employees); // {Mick=51, David=33, Trent=55, Sameer=36}
  • compute: Compute function always computes the new value whether Key is already present in the Map or not 
    employees.compute("Casey", (k, v) -> 67);
    employees.compute("David", (k, v) -> 33);
    
    System.out.println(employees); // {Mick=51, Casey=67, David=33, Trent=55, Sameer=36}

Other Utility Methods 

There are a few more new utility methods they are 'forEach', 'replaceAll'. 

The 'forEach' accepts 'BiConsumer' and using 'forEach' makes the code more concise. 

The 'replaceAll' function helps in replacing each entries value. In our case let's increase the age of all the employees by 10 years 

employees.replaceAll((name, age) -> age + 10);
employees.forEach((k, v) -> System.out.println(k + "-" + v));