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"))
Answer from Grzegorz Piwowarek on Stack OverflowThe 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"))
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.
Videos
With streams you generally want to avoid creating empty lists and then adding items. Streams should use functional idioms and avoid side effects as much as possible. It's better to work with the stream as a whole and then "collect" the results into a list at the end.
List<C> value = getResponses().stream()
.flatMap(r -> getValues(r).stream())
.collect(Collectors.toList());
I am trying to convert below for-loop to forEach method with help Stream function using Java 1.8.
You shouldn't use a stream along with forEach simply to accumulate into a predefined list as there will be side effects (which should be avoided when dealing with streams), rather go with the stream approach suggested by John Kugelman if you want to perform it with streams or using the forEach method it can also be done as:
List<A> value = new ArrayList<>();
responseList.forEach(response -> value.addAll(getValues(response))));
Instead of using a forEach just use streams from the beginning:
List<PersonWrapper> wrapperList = jrList.stream()
.flatMap(jr -> seniorList.stream()
.filter(sr -> jr.getName().equals(sr.getName()))
.map(sr -> new PersonWrapper(jr, sr))
)
.collect(Collectors.toList());
By using flatMap you can flatten a stream of streams (Stream<Stream<PersonWrapper>>) into a single stream (Stream<PersonWrapper>)
If you can't instantiate wrapperList by yourself or really need to append to it. You can alter above snippet to following:
List<PersonWrapper> wrapperList = new ArrayList<>();
jrList.stream()
.flatMap(jr -> seniorList.stream()
.filter(sr -> jr.getName().equals(sr.getName()))
.map(sr -> new PersonWrapper(jr, sr))
)
.forEach(wrapperList::add);
While Lino's answer is certainly correct. I would argue that if a given person object in jrList can only ever have one corresponding match in seniorList maximum, in other words, if it's a 1-1 relationship then you can improve upon the solution given by Lino by finding the first match as follows:
List<PersonWrapper> resultSet = jrList.stream()
.map(p -> seniorList.stream()
.filter(sr -> p.getName().equals(sr.getName()))
.findFirst()
.map(q -> new PersonWrapper(p, q))
.get())
.collect(Collectors.toList());
or if there is no guarantee that each person in jrList will have a corresponding match in seniorList then change the above query to:
List<PersonWrapper> resultSet = jrList.stream()
.map(p -> seniorList.stream()
.filter(sr -> p.getName().equals(sr.getName()))
.findFirst()
.map(q -> new PersonWrapper(p, q))
.orElse(null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
The difference is that now instead of calling get() on the result of findFirst() we provide a default with orElse in case findFirst cannot find the corresponding value and then we filter the null values out in the subsequent intermediate operation as they are not needed.