Prerequisite: Understanding of Java 8 streams.
Q. You have a list of key value pairs (“student name”, “subject”) as shown below.
Input
|
1 2 3 4 5 6 7 |
("Sam Peter", "English"), ("Sam Peter", "Science"), ("John English", "Science"), ("Neil Graham", "Maths"), ("Neil Graham", "Arts"), ("Neil Graham", "Music") |
The required task is to list all the subjects for each student as shown below.
Result
|
1 2 3 4 |
"Samp Pter" --> [English, Science] "John English" --> [Science] "Neil Graham" --> [Maths, Arts, Music] |
A. Here is the Java 8 example with lambda expressions, and then let’s discuss this in detail.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package com.java8.examples; import java.util.Arrays; import java.util.List; import java.util.Map; import static java.util.stream.Collectors.*; public class KeyValuePair<K,V> { private final K key; private final V value; public K getKey() { return key; } public V getValue() { return value; } public KeyValuePair(K key, V value) { this.key = key; this.value = value; } public static void main(String[] args) { List<KeyValuePair<String,String>> studentSubjects = Arrays.asList( new KeyValuePair<String, String>("Sam Peter", "English"), new KeyValuePair<String, String>("Sam Peter", "Science"), new KeyValuePair<String, String>("John English", "Science"), new KeyValuePair<String, String>("Neil Graham", "Maths"), new KeyValuePair<String, String>("Neil Graham", "Arts"), new KeyValuePair<String, String>("Neil Graham", "Music") ); Map<String, List<String>> result = studentSubjects.stream() .collect(groupingBy(KeyValuePair::getKey, mapping(KeyValuePair::getValue, toList()))); System.out.println(result); } } |
Key Points
1) The key and value objects are marked as final. One of the tenets of FP is immutability.
2) The “KeyValuePair” class is written with generics to take any data type. In this example K is of type String, and V is of type String. In fact the “main” method should be moved to a separate class of its own.
3) Note the static import of “Collectors”, which performs reduction operations, such as accumulating elements into collections, summarizing elements according to various criteria, etc.
|
1 |
import static java.util.stream.Collectors.*; |
Which is used for methods “groupingBy”, “mapping”, and “toList” methods. This can be rewritten with “Collectors” as shown below
|
1 2 3 4 5 6 7 |
Map<String, List<String>> result = studentSubjects.stream() .collect(Collectors.groupingBy(KeyValuePair::getKey, Collectors.mapping(KeyValuePair::getValue, Collectors.toList()))); |
Q. Why the forEach, collect, mapping, etc methods not added to java.util.List nor the java.util.Collection interface ?
A. stream( ) is a default method added to the Collection interface in Java 8. The stream( ) returns a java.util.Stream interface with multiple abstract methods like filter, map, sorted, collect, etc.
If these were added to the Collection interface and once published, it is impossible to add new methods to an interface without breaking the existing implementation. Hence, “default” methods were introduced in Java 8. From Java 8 onwards, implementation methods can now be added to interfaces providing a default implementation of the declared behavior.
Q. Why these methods were added to Stream and Collector instead of adding as a default method to the Collection class?
A. Due to backward compatibility. Many existing projects extend pre Java 8 implementation of “Collection” and should be refactored.