Collectors
Collect
Collect the data into a list using the mose used method
collect(Collectors.toList())
Pass a kep mapper function adn value mapper function to create a map
.collect(Collectors.toMap(Function.identity(), String::length))
If data need to be passed to a client, its a good idea to use unmodifiable
collection
.collect(Collectors.toUnmodifiableList())
Returns an immutable list containing only one specified object
Collections.singletonList(s)
String s = parentDto.getStringList().get(0);
parentDto.setStringList(Collections.singletonList(s));
Partitioning
Split the collection into 2 groups.
Collectors.partitioningBy
accepts a predicate and returns a map with one key
for true
with all the values
with true results and another with false results.
List<Integer> list = Arrays.asList(1,2,1,3,3,4,5,6,7,8,6,5,4,3,2,1);
Map<Boolean, List<Integer>> collect = list.stream()
.collect(partitioningBy(number -> number % 2 == 0));
System.out.println(collect);//{false=[1, 1, 3, 3, 5, 7, 5, 3, 1], true=[2, 4, 6, 8, 6, 4, 2]}
Predicate can be extracted out and can be passed as an argument
Map<Boolean, List<Integer>> listMap = employees.stream()
.filter(Objects::nonNull).filter(emp -> null != emp.getAge())
.filter(emp -> null != emp.getAge())
//.collect(partitioningBy(x -> evenAgedEmpPredicate.test(x)));
.collect(partitioningBy(evenAgedEmpPredicate));
GroupingBy
Collectors.groupingBy
takes a function as first parameter that determines the
key od the return map
and another Collector that can have the vlaues. The collector can be another
operation that returns a collector
like filtering, mapping, filtering etc.
List<Integer> list = Arrays.asList(1,2,1,3,3,4,5,6,7,8,6,5,4,3,2,1);
//Find frequency of all the numbers
Map<Integer,Long> map = list.stream()
//.collect(groupingBy(element -> element, counting()));// Function.identity() Equivalent to an i in a for loop
.collect(Collectors.groupingBy(Function.identity(), counting()));//collect takes a COLLECTOR as parameter. any method that returns a collector can be used
System.out.println(map);//{1=3, 2=2, 3=3, 4=2, 5=2, 6=2, 7=1, 8=1}
map (with streams) takes a Stream<T>
returns Stream<R>
. It transforms from
one style to another
mapping (with collectors) -> transforming in the middle of reduce
filter -> Acts on Streams, filtering -> Acts on reduce operation with collector
Filtering
Collectors.filtering()
takes a predicate as a first argument and another
Collector as second argument.
List<Integer> list = List.of(1,2,1,3,3,4,5,6,7,8,6,5,4,3,2,1);
List<Integer> evenNumberList = list.stream()
.collect(filtering(number -> number % 2 == 0, toList()));
System.out.println(evenNumberList);//[2, 4, 6, 8, 6, 4, 2]
Mapping
Collectors.mapping
takes a function (as first parameter) based on which the
transformation happens
and a Collector as second parameter
List<Integer> list = List.of(1,2,1,3,3,4,5,6,7,8,6,5,4,3,2,1);
List<Integer> doubleNumberList = list.stream()
.distinct()//Finds unique elements
.collect(Collectors.mapping(number -> number * 2 , Collectors.toList()));
System.out.println(doubleNumberList);//[2, 4, 6, 8, 10, 12, 14, 16]
Flat Mapping
flatMapping applies the map first and then does the flattening.
Collectors.flatMapping
takes a Stream (like an iterator)
as first input and takes Collector as second parameter. With the mapping
function, use of Stream.of or collections.stream()
The method signatures of map from Stream and flatMap of Collectors looks like below
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
<R> Stream<R> map (Function<? super T, ? extends R> mapper)
List<String> list = List.of("one", "two wings", "three tyres", "four turbo combustion engine");
//Fnd a list of each word separated without space
List<String> collect = list.stream()
.collect(
flatMapping(str -> Stream.of(str.split(" ")), toList()
)
);
System.out.println(collect);//[one, two, wings, three, tyres, four, turbo, combustion, engine]
Do’s and Scenarios
- If you have a one-to-one function , use a map to go from
Stream<T>
toStream<R>
List<Integer> numbers = List.of(1,2,3,4);
//one-to-one function
//Stream<T>.map(oneToOneFunction) ==> Stream<R>
List<Integer> collect = numbers.stream()
.map(element -> element * 2)//Takes a Stream of <T> and returns a Stream of <R>
.collect(Collectors.toList());
System.out.println(collect);//[2, 4, 6, 8]
- If you have a one-to-many function , use a map to go from
Stream<T>
toStream<Collection<R>>
//one-to-many
//Stream<T>.map(oneToManyFunction) ==> Stream<List<R>>
List<List<Integer>> collect = numbers.stream()
.map(element -> List.of(element + 1, element - 1))
.collect(Collectors.toList());
System.out.println(collect);//[[2, 0], [3, 1], [4, 2], [5, 3]]
//use Case : Given a list of employees, give the personal email id and official email id as pair
- If you have a one-to-many function , use a flatMap to go from
Stream<T>
toStream<R>
//one-to-many function
//Stream<T>.map(oneToManyFunction) ==> Stream<R> (not Stream of List of R)
List<Integer> numbers = List.of(1,2,3,4);
List<Integer> collect = numbers.stream()
.flatMap(element -> List.of(element + 1, element - 1).stream())
.collect(Collectors.toList());
System.out.println(collect);//[2, 0, 3, 1, 4, 2, 5, 3]
For the next sections the following object structure and values are used
EmployeeSimple(name=John, age=20, salary=65000.0, level=C, experience=5)
EmployeeSimple(name=Wayne, age=20, salary=65430.0, level=C, experience=4)
EmployeeSimple(name=Dow, age=30, salary=74445.0, level=B, experience=6)
EmployeeSimple(name=Jane, age=35, salary=76546.0, level=B, experience=5)
EmployeeSimple(name=Don, age=35, salary=90000.0, level=A, experience=10)
EmployeeSimple(name=Wayne, age=20, salary=65430.0, level=C, experience=4)
EmployeeSimple(name=John, age=23, salary=75430.0, level=B, experience=5)
EmployeeSimple(name=John, age=32, salary=85430.0, level=C, experience=12)
EmployeeSimple(name=null, age=null, salary=null, level=