The Benefits of the Collection API in Java 8: Part 2

Oct 11, 2019
hackajob Staff

In part one of this article, we covered the ‘forEach’ method, the ‘Collection.removeIf’ method, the ‘Iterator.forEachRemaining’ method and the ‘SplitIterator’ interface. For part two, we’ll be looking at the improvements made on both the ‘List’ and ‘Map’ interfaces.

List Improvements

Java 8 has added some methods on the ‘List’ interface which make it easy to perform operations on Lists - We’ve detailed some of these below:

List.sort

The ‘List.sort’ method can be used to sort a ‘List’ based on the ‘Comparator’ passed in.

The following code demonstrates this:

List<Integer> input = Arrays.asList(99,12,54,76,23);

input.sort((num1,num2) -> num1 - num2);

input.forEach(num -> System.out.print(num+" "));

Above, the ‘List.sort’ method sorts the input ‘List’ in ascending order. Prior to Java 8, the ‘Collections.sort’ could be used to sort the elements in a ‘List’. ‘List.sort’ is far superior in comparison ‘Collections.sort’ method, because ‘Collections.sort’ first dumps the contents into a primitive array, sorts it and then copies this back to the overall ‘Collection’. ‘List.sort’ on the other hand sorts everything in one place.

The ‘List.sort’ method accepts a ‘Comparator’ instance, meaning it sorts the ‘List’ based on the ‘Comparator’ instance passed in. Here, the ‘Comparator’ is implemented via a lambda expression that returns the difference of the input numbers. So when this code is executed, it will print the following output:

12 23 54 76 99

List.replaceAll

Java 8 recently added a method called ‘replaceAll’ as part of the ‘List’ interface, which can be used to replace all the elements in a ‘List’. The following code shows how this works:

List<String> input = Arrays.asList("apple", "mango", "orange");

input.replaceAll(str -> str.toUpperCase());

input.forEach(str -> System.out.print(str+" "));

Here, the ‘replaceAll’ method converts all the ‘Strings’ in the input ‘List’ to uppercase. Before Java 8, if you wanted to replace all the elements in a ‘List’, you had to write a 'for' loop and manually replace each element in the ‘List’. In comparison, this code is far more concise and easier to read.

The ‘replaceAll’ method accepts a ‘UnaryOperator’ interface as a parameter. In the code above, the ‘UnaryOperator’ is implemented via a lambda expression that accepts a ‘String’ value, converts it to uppercase and returns it. The ‘replaceAll’ method applies this lambda expression to every element in the input map. So when this code is executed, it will print the following output:

APPLE MANGO ORANGE

Map improvements

Java 8 has added several other convenience-led methods to the Map interface also. We’ve explained some of these in the next few sections:

getOrDefault

The ‘map.getOrDefault’ method returns the value corresponding to the specified key if present. If there is no value corresponding to the key, it returns the specified default value. In comparison, the ‘map.get’ method returns a null if there is no value associated with the specified key.

The following code demonstrates this:

Map<Integer,String> colours = new HashMap<Integer,String>();

colours.put(1, "Red");

colours.put(2, "Blue");

String colour1 = colours.getOrDefault(1, "white");//returns red

System.out.println("colour1:"+colour1);

String colour3 = colours.getOrDefault(3, "white"); //returns white

System.out.println("colour1:"+colour3);

The ‘getOrDefault’ method accepts parameters corresponding to the key and a default value. Since the key “1” is mapped to the value “Red”, this value will be returned. However, there is no mapping corresponding to “3”, so the default value, that is “white” will be returned. When this code is executed, it will print the following to the console:

colour1:Red

colour3:white

putIfAbsent

The ‘Map.putIfAbsent’ method adds the specified key value, however it will only do this if the map does not contain the specified key. If the ‘map’ contains the specified key, it returns the current value that the key is mapped to. This is different to the ‘map.put’ method which replaces the current value with the new value if the key is already present in the map.

The following code demonstrates this:

Map<Integer,String> colours = new HashMap<Integer,String>();

colours.put(1, "red");

colours.put(2, "blue");

colours.putIfAbsent(2, "yellow"); // will not do anything!!

System.out.println("colour2:"+colours.get(2));

colours.putIfAbsent(3, "brown"); // will add this to map!

System.out.println("colour3:"+colours.get(3));

The ‘putIfAbsent’ method accepts parameters corresponding to the key and the value to be added. The first call to ‘putIfAbsent’ tries to add the key “2” with the value “yellow”. Since there is already a value “blue” corresponding to the key “2”, the ‘putIfAbsent’ method will not replace it, so the value for key “2” will still remain as “blue”. The second call to ‘putIfAbsent’ tries to add “3-brown” to the map. Since there is no key-value pair in the map corresponding to “3”, the ‘putIfAbsent’ method will add this to the map instead.

When this code is executed, it will print the following output:

colour2:blue

colour3:brown

replace

The ‘Map.replace’ method replaces the value for the specified key, but only if it's mapped to a value. There’s also an overloaded version of the replace method that replaces the specified key; again this is only if it's mapped to the specified value. The following code demonstrates this:

Map<Integer,String> colours = new HashMap<Integer,String>();

colours.put(1, "red");

colours.put(2, "blue");

colours.replace(1,"yellow"); //replaces red with yellow

System.out.println(colours.get(1));

colours.replace(2,"green","black"); //does nothing!

System.out.println(colours.get(2));

colours.replace(2,"blue","black"); //replaces blue with black

System.out.println(colours.get(2));

The first call to the replace method accepts a key and value. In this case, the map contains the key “1”, so its value is replaced with “yellow”.

The second call to the ‘replace’ method accepts a key, old value and new value. Since there is no key value mapping corresponding to “2-green”, this call does nothing. The third call also accepts a key, old value and new value. Because there is a key-value mapping corresponding to “2-blue”, it replaces the value “blue” with “black". So when this code is executed, it will print the following output:

yellow

black

replaceAll

The ‘Map.replaceAll’ is similar to the ‘List.replaceAll’. It can be used to apply some function to all the elements in a ‘Map’ and replace their values.

The following code demonstrates this:

Map<Integer,String> colours = new HashMap<Integer,String>();

colours.put(1, "red");

colours.put(2, "blue");

colours.replaceAll((key,val) -> val.toUpperCase());

colours.forEach((key,val) -> System.out.println(val+” “));

Here, the ‘replaceAll’ method converts each value in the ‘Map’ to uppercase.

The ‘replaceAll’ method accepts the ‘BiFunction’ interface as a parameter . In the code above, the ‘BiFunction’ interface is implemented via a lambda expression that accepts the key and value, converts the value to uppercase and returns it. The ‘replaceAll’ method applies this lambda expression to every element in the input map. When this code is executed, the following output will be printed:

RED BLUE

Compute

The ‘Map.compute’ method can be used to compute a new value for a particular key. If there’s no entry corresponding to the specified key, the ‘compute’ method still computes the value and adds it to the map.

The following code demonstrates this:

Map<Integer,String> colours = new HashMap<Integer,String>();

colours.put(1, "Red");

colours.put(2, "Blue");

colours.compute(2, ((id,colour) -> id+":"+colour) );

System.out.println(colours.get(2));

colours.compute(3, ((id,colour) -> id+":Yellow") );

System.out.println(colours.get(3));

Here, the ‘compute’ method replaces the value field with a new value which consists of the id appended with the value.

The ‘compute’ method accepts the ‘BiFunction’ interface as a parameter. In the code above, the ‘BiFunction’ interface is implemented via a lambda expression that accepts the key and value, appends both of them and returns the result.

So when this code is executed, it will print the following output:

2:Blue

3:Yellow

When the compute method is invoked on the map entry with the key as “2” it directly replaces its value. When the compute method is invoked for the key “3”, there is no map entry with the key as “3”. Instead, the code computes the value for the key as per the specified lambda expression and adds it to the map.

computeIfPresent/computeIfAbsent

In addition to compute, there is a method called ‘computeIfPresent’ which is added to the ‘Map’ interface also. This is similar to the ‘compute’ method, but it computes a value for the specified key only if the key is present in the map. So if the key is not present in the ‘Map’, it does not add it to the ‘Map’. There is also a ‘computeIfAbsent’ method. The ‘computeIfAbsent’ method computes a value for the specified key only if the key is not present in the map.

remove

Prior to Java 8, there was already a ‘remove’ method on the ‘Map’ interface that could be used to remove the map entry corresponding to the specified key. Java 8 has added an overloaded version of the ‘remove’ method where you can specify a value as well. So this ‘remove’ method removes the specified key, but only if it is mapped to the specified value. The following code demonstrates this:

Map<Integer,String> colours = new HashMap<Integer,String>();

colours.put(1, "red");

colours.put(2, "blue");

colours.put(3, "yellow");

colours.remove(1,"red"); //removes key 1 only if it’s value is “red”

colours.remove(2,"black");//removes key 2 only if it’s value is                 “black”

System.out.println(colours.get(1));

System.out.println(colours.get(2));

In this case, the ‘remove’ method first tries to remove key “1” with the value “red”. Since there is such a key-value mapping, this entry will be removed from the map. It then tries to remove key “2” with the value “yellow”. Since key “2” has the value “black” in the map, this entry is not removed. So when this code is executed, it will print the following output:

null

Blue

In this article, we saw some of the improvements made by Java 8 on both the ‘List’ and ‘Map’ interfaces. Overall, there have been lots of positive improvements made to Java 8, which is why it’s one of our favourite development programmes to work with.

Like what you’ve read? Make sure to check out our other Java articles.