Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot make filter->forEach->collect in one stream?

I want to achieve something like this:

items.stream()     .filter(s-> s.contains("B"))     .forEach(s-> s.setState("ok")) .collect(Collectors.toList()); 

filter, then change a property from the filtered result, then collect the result to a list. However, the debugger says:

Cannot invoke collect(Collectors.toList()) on the primitive type void.

Do I need 2 streams for that?

like image 507
nimo23 Avatar asked Aug 21 '17 09:08

nimo23


2 Answers

The forEach is designed to be a terminal operation and yes - you can't do anything after you call it.

The idiomatic way would be to apply a transformation first and then collect() everything to the desired data structure.

The transformation can be performed using map which is designed for non-mutating operations.

If you are performing a non-mutating operation:

 items.stream()    .filter(s -> s.contains("B"))    .map(s -> s.withState("ok"))    .collect(Collectors.toList()); 

where withState is a method that returns a copy of the original object including the provided change.


If you are performing a side effect:

items.stream()   .filter(s -> s.contains("B"))   .collect(Collectors.toList());  items.forEach(s -> s.setState("ok")) 
like image 62
Grzegorz Piwowarek Avatar answered Oct 02 '22 17:10

Grzegorz Piwowarek


Replace forEach with map.

 items.stream()       .filter(s-> s.contains("B"))       .map(s-> {s.setState("ok");return s;})       .collect(Collectors.toList()); 

forEach and collect are both terminal operations - Streams must have just one. Anything that returns a Stream<T> is a intermediate operation, anything other is a terminal operation.

like image 45
Eugene Avatar answered Oct 02 '22 17:10

Eugene