Home > front end >  Collect results of forEach in Java
Collect results of forEach in Java

Time:07-20

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();
}
  •  Tags:  
  • java
  • Related