Advance Streams Operations
Plethora of different operations available with streams. Some basic and very important operations are already been discussed in previous article (read it from here). Let’s see how collect, flatMap and reduce operations works.
How to use Collect operation in streams?
Collect is used to transform the elements of the stream into a different data structures such as List, Set and Map. Collect accepts a collector which is based on operations like supplier, accumulator, combiner and finisher. The following example shows how to construct a list from stream elements and prints the result on the console.
package java8; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Student { String name; int age; Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name; } public static void main(String[] args){ //Create an list of student of objects ArrayList stdList = new ArrayList(); stdList.add(new Student("Peter",22)); stdList.add(new Student("Sara",23)); stdList.add(new Student("Danial",21)); stdList.add(new Student("Siemen",22)); //use stream operation to filter student name started with S List filtered = stdList .stream() .filter(s -> s.name.startsWith("S")) .collect(Collectors.toList()); //Print the list filtered using stream operation System.out.println(filtered); } }
Output :
[Sara, Siemen]
Another example used to group student and construct a list from streams elements, averaging the student age and displaying. In another example, a more comprehensive statistics is been returned by a summarizing collector containing min, max, total number of students, sum on age and average age of student. Further example is using join the student who met a specific condition and using delimiter to separate each student.
package java8; import java.util.ArrayList; import java.util.IntSummaryStatistics; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Student { String name; int age; Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name; } public static void main(String[] args) { // Create an list of student of objects ArrayList stdList = new ArrayList(); stdList.add(new Student("Peter", 22)); stdList.add(new Student("Sara", 23)); stdList.add(new Student("Danial", 21)); stdList.add(new Student("Siemen", 22)); // Stream operation to group students by age Map<Integer, List> studentsByAge = stdList.stream().collect(Collectors.groupingBy(s -> s.age)); // Print student group one by one studentsByAge.forEach((age, s) -> System.out.format("Student Age %s: %s\n", age, s)); // Compute average age of students Double averageAge = stdList.stream().collect(Collectors.averagingInt(s -> s.age)); System.out.println("Average Age : " + averageAge); // Overall age statistics IntSummaryStatistics ageSummary = stdList.stream().collect(Collectors.summarizingInt(s -> s.age)); System.out.println("Age Summary: " + ageSummary); // Join by same age String age = stdList.stream().filter(s -> s.age <= 22).map(s -> s.name) .collect(Collectors.joining(" and ", " ", " are 22 year old or less.")); System.out.println("Join Person by Age: " + age); // Mapping key and values together Map<Integer, String> map = stdList.stream() .collect(Collectors.toMap(s -> s.age, s -> s.name, (name1, name2) -> name1 + ";" + name2)); System.out.println("Mapping keys and Values together : " + map); } }
Output
Student Age 21: [Danial] Student Age 22: [Peter, Siemen] Student Age 23: [Sara] Average Age : 22.0 Age Summary: IntSummaryStatistics{count=4, sum=88, min=21, average=22.000000, max=23} Join Person by Age: Peter and Danial and Siemen are 22 year old or less. Mapping keys and Values together : {21=Danial, 22=Peter;Siemen, 23=Sara}
Another more complicated example is to build your own special collector. In the example below we transformed the stream into a single String containing all names in upper letter and separated by comma delimiter. We used all four ingredients such as supplier, accumulator, combiner and finisher.
package java8; import java.util.ArrayList; import java.util.StringJoiner; import java.util.stream.Collector; public class Student { String name; int age; Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name; } public static void main(String[] args) { // Create an list of student of objects ArrayList stdList = new ArrayList(); stdList.add(new Student("Peter", 22)); stdList.add(new Student("Sara", 23)); stdList.add(new Student("Danial", 21)); stdList.add(new Student("Siemen", 22)); Collector<Student, StringJoiner, String> stdNameCollector = Collector.of( () -> new StringJoiner(", "), // supplier (i, s) -> i.add(s.name.toUpperCase()), // accumulator (x, y) -> x.merge(y), // combiner StringJoiner::toString); // finisher String names = stdList .stream() .collect(stdNameCollector); System.out.println(names); } }
Output
PETER, SARA, DANIAL, SIEMEN
How to use FlatMap Operations?
FlatMap returns a stream which consist of the results, came from replacing each element of current stream with another mapped stream. Map has a limitation that can map exactly one other object. FlatMap can be used to overcome this kind of shortcomings and one object is transformable to multiple others.
In below example we are using two classes Student and Register. We used stream to instantiate Student and Register objects in the main method of the program. The we created three classes for every single student.
package java8; import java.util.ArrayList; import java.util.stream.IntStream; public class Student { public String name; int age; Student(String name) { this.name = name; } Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name; } public static void main(String[] args) { ArrayList stClass = new ArrayList<>(); // create classes IntStream.range(1, 4).forEach(i -> stClass.add(new Register("class" + i))); // Register students stClass.forEach( std -> IntStream.range(1, 4).forEach(i -> std.std.add(new Student("Std" + i + " <- " + std.stdClass)))); stClass.stream().flatMap(s -> s.std.stream()).forEach(s1 -> System.out.println(s1.name)); } } class Register { String stdClass; ArrayList std = new ArrayList<>(); Register(String stdClass) { this.stdClass = stdClass; } }
Output
Std1 <- class1 Std2 <- class1 Std3 <- class1 Std1 <- class2 Std2 <- class2 Std3 <- class2 Std1 <- class3 Std2 <- class3 Std3 <- class3
How reduce works in stream?
The reduction operation combines all elements of the stream into a single result.
package java8; import java.util.ArrayList; import java.util.stream.IntStream; public class Student { public String name; int age; Student(String name){ this.name=name; } Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name; } public static void main(String[] args) { ArrayList std = new ArrayList<>(); std.add(new Student("A",21)); // create classes IntStream .range(1, 5) .forEach(i -> std.add(new Student("s" + i,22))); //Return student name with maximum age std .stream() .reduce((s1, s2) -> s1.age > s2.age ? s1 : s2) .ifPresent(System.out::println); } }
Output
s4
The following method accepts both identity value and a BinaryOperator accumulator and aggregate names and ages of all student in stream.
package java8; import java.util.ArrayList; import java.util.stream.IntStream; public class Student { public String name; int age; Student(String name) { this.name = name; } Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name; } public static void main(String[] args) { ArrayList std = new ArrayList<>(); std.add(new Student("A1", 21)); // create classes IntStream.range(1, 5).forEach(i -> std.add(new Student("s" + i, 22))); Student result = std.stream().reduce(new Student("", 0), (s1, s2) -> { s1.age += s2.age; s1.name += s2.name; return s1; }); System.out.format("name=%s; age=%s", result.name, result.age); } }
Awesome work. Keep it up.
Visit us for up to date HDR algorithms, image processing and bio informatics tutorials.
http://informatics-lab.com/