-
Enhancement
-
Resolution: Fixed
-
P4
-
None
-
None
-
None
Sometimes it is needed to have a single 'bottleneck' operation in between 2 for loops. For instance:
List<I> intermediates = new ArrayList<>();
for (E element : elements) {
// do something for each element
I i = ...
intermediates.add(i);
}
bottleneckOperation();
for (I element : intermediates) {
// do another operation for each element
}
Where it can be important that the bottleneck operation takes place after the first loop is finished executing.
(a real world example would be the first loop submitting tasks to a StructuredTaskScope, calling scope.join() as the bottleneck operation, and then calling future.get() on each future produced by the first loop, in the second loop)
To do this with streams, an 'intermediate' terminal operation would be required (toList() in this example):
List<I> intermediates = elements.stream().map(e -> ...).toList();
bottleneckOperation();
intermediates.stream().forEach(i -> ...);
The fact that a List has to be used to store the intermediate values is an irrelevant implementation detail to a writer or reader of this code. Furthermore, some might wonder why 'intermediates' isn't a `Stream<I>` to avoid having to call `.stream()` on it again later. But that would mean the elements are not processed by the 'map' before calling the bottleneck operation.
The suggestion is to add something like a `Stream::bottleneck(Runnable)` method, that hides the implementation details of the above code. The result would be:
elements.stream()
.map(e -> ...)
.bottleneck(() -> bottleneckOperation())
.forEach(i -> ...);
List<I> intermediates = new ArrayList<>();
for (E element : elements) {
// do something for each element
I i = ...
intermediates.add(i);
}
bottleneckOperation();
for (I element : intermediates) {
// do another operation for each element
}
Where it can be important that the bottleneck operation takes place after the first loop is finished executing.
(a real world example would be the first loop submitting tasks to a StructuredTaskScope, calling scope.join() as the bottleneck operation, and then calling future.get() on each future produced by the first loop, in the second loop)
To do this with streams, an 'intermediate' terminal operation would be required (toList() in this example):
List<I> intermediates = elements.stream().map(e -> ...).toList();
bottleneckOperation();
intermediates.stream().forEach(i -> ...);
The fact that a List has to be used to store the intermediate values is an irrelevant implementation detail to a writer or reader of this code. Furthermore, some might wonder why 'intermediates' isn't a `Stream<I>` to avoid having to call `.stream()` on it again later. But that would mean the elements are not processed by the 'map' before calling the bottleneck operation.
The suggestion is to add something like a `Stream::bottleneck(Runnable)` method, that hides the implementation details of the above code. The result would be:
elements.stream()
.map(e -> ...)
.bottleneck(() -> bottleneckOperation())
.forEach(i -> ...);