I want to collect the values returned by a method called inside a forEach
:
@PostMapping(value="insert-ppl")
public String insertPeople(@RequestBody @Valid @NotNull List<Person> people){
people.forEach(this::insertPerson);
}
the insertPerson
method returns a string signaling whether the insert in the database was successfull or not. I want to take the strings returned from each call to the insertPerson
. But I can't use the .collect
because is not a stream.
How can I do that?
CodePudding user response:
The forEach
is supposed to be used to execute a consuming operation on each element. It's not supposed to be used to collect any results.
For this purpose you need to do it differently. I think best option would be to just use a standard for
loop and add result of each insertPerson
call into a list that you then return at the end.
Theoretically, you could use something like people.stream().map(this::insertPerson).toList()
but that's in my opinion not great idea, because map()
is supposed to just map input to output without any side effects, however your insertPerson
obviously has side effects, which may make it more "problematic" for a reader to understand what is really happening.
CodePudding user response:
There's a couple of reasons why this code isn't suitable for streams.
Firstly, your goal is to operate via side-effects (because you need to update the database), the only operations which are intended for such cases are forEach()
and forEachOrdered()
. And as API documentation worns these methods should be used with great care.
Note that map()
isn't suitable for that purpose, it wasn't designed to operate via side-effects. And intermediate operations like map()
in some cases could be optimized away from the stream. It's not likely to happen in a pipeline build like that: stream().map().collect()
, but the key point is that intermediate operations semantically are not intended to perform resulting actions, in your case call insertPerson()
is a resulting action and accumulated string is only a byproduct.
For your purposes, you can still use Iterable.forEach()
.
@PostMapping(value = "insert-ppl")
public String insertPeople(@RequestBody @Valid @NotNull List<Person> people) {
StringJoiner result = new StringJoiner("\n"); // provide the required delimiter instead of "\n"
people.forEach(person -> result.add(insertPerson(person)));
return result.toString();
}