How to process forEach in Java 8 using streams when I want to have multiple results from the iteration. Of course calling List.add method in stream forEach operation is not an option... How would you rewrite this code using Java 8 streams?
additionalWebsitesList = Lists.newArrayList();
String additionalWebistesCsv = "";
for (String website : additionalWebsites) {
WebsiteModel websiteModel = getWebsiteModel(website);
//How this can be processed via Stream???
additionalWebsitesList.add(websiteModel);
areaModels.addAll(websiteModel.getAreas());
additionalWebistesCsv += websiteModel.getId();
}
The way to do things like this with streams is to use a custom collector. Suppose we define the following class:
public class Results {
private final List<WebsiteModel> additionalWebsitesList = new ArrayList<>();
private final List<AreaModel> areaModels = new ArrayList<>();
private final StringBuilder additionalWebsitesCsv = new StringBuilder();
public void accumulate(WebsiteModel websiteModel) {
additionalWebsitesList.add(websiteModel);
areaModels.addAll(websiteModel.getAreas());
additionalWebsitesCsv.append(websiteModel.getId());
}
public void combine(Results another) {
additionalWebsitesList.addAll(another.additionalWebsitesList);
areaModels.addAll(another.areaModels);
additionalWebsitesCsv.append(another.additionalWebsitesCsv);
}
public List<WebsiteModel> getAdditionalWebsitesList() {
return additionalWebsitesList;
}
public List<AreaModel> getAreaModels() {
return areaModels;
}
public String getAdditionalWebsitesCsv() {
return additionalWebsitesCsv.toString();
}
}
With this Results
class in place, you're ready to collect the results:
Results results = additionalWebsites.stream()
.map(this::getWebsiteModel)
.collect(Results::new, Results::accumulate, Results::combine);
This uses the Stream.collect
method. The first argument is a Supplier
that creates the mutable object into which elements of the stream are to be accumulated via the accumulator provided in the second argument. The third argument is a merger that will only be used to combine partial results if the stream is parallel.
What is the best practice for managing Java 8 streams with multiple results
Of course calling List.add method in stream forEach operation is not an option.
The best practice is not forcing you to use streams as the use case is not appropriate.
The Stream
interface javadoc describes itself as :
A sequence of elements supporting sequential and parallel aggregate operations.
Stream
operations return themselves a sequence of typed elements.
So Stream
collects will return finally a single kind of thing based on the type of the sequence of elements : unitary or a collection of.
We could for example collect a Foo
, a List
of Foo
, a Map
of Foo
indexed by Integer
keys, and so for...
Stream
collects are not designed to collect different custom things such as a List
of Foo
+ a List
of Bar
+ the count of Bar
+ Foo
.
As @Eugene underlined, Streams also provide a way to get IntSummaryStatistics
that is a set of things : common aggregates that are minimum, maximum, sum, and average. But should we not consider that it collects finally a single thing : statistics ?
So you have three ways :
putting aside Stream
for your use case and keeping the for
loop.
using multiple streams : one stream by custom thing that you want to collect.
using a single stream with a custom collector.
I would not use the second way in any case as it will produce a less readable and straight code that your actual.
About the last way (custom collector) illustrated by the Federico Peralta Schaffner answer. It is straighter and it allows to benefit from stream parallelism. So it is an option to consider.
But it also requires more boiler plate code and has more reading indirection to understand the actual logic.
So I think that I would introduce a custom collector only in two cases :
And in any other cases, I would keep the for
loop.
That code as Java 8 streams:
additionalWebsitesList = additionalWebsites.stream()
.map(this::getWebsiteModel)
.collect(Collectors.toList());
areaModels = additionalWebsites.stream()
.flatMap(w -> getWebsiteModel(w).getAreas().stream())
.collect(Collectors.toList());
String additionalWebistesCsv = additionalWebsites.stream()
.map(this::getWebsiteModel)
.map(WebsiteModel::getId)
.collect(Collectors.joining());
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.