Reputation: 7750
In java 8,java.util.stream.Stream#forEach
consider as a replacement for traditional for-loop. But why not this is not chain function.Says it return void instead of Stream<T>
it self.
Like this
Arrays
.stream(Girls.toArray())
.forEach(Girls::getUp)
.forEach(Girls::dressUp)
.filter(/* Top 10 Girls */)
.forEach(Gay.getMe()::gotGirl)
.endFilter()// Not an API, but it means remove last filter
.filter(/* Worst 10 Girls */)
.forEach(Gay.get(0)::gotGirl)
girls = Arrays
.stream(Girls.toArray());
girls.forEach(g->{g.getUp();g.dressUp()});
girls.filter(/* Top 10 Girls */)
.forEach(Gay.getMe()::gotGirl);
girls.filter(/* Worst 10 Girls */)
.forEach(Gay.get(0)::gotGirl);
The first is nice than second one.But the first one got worse performance.
So, why forEach
is not chainable ?
Upvotes: 11
Views: 6611
Reputation: 3344
As andThen
is defined in Java 8 as
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
you can easily create your own composite Consumer<T>
for any number of your functions
class CompositeConsumer<T> implements Consumer<T> {
private final List<Consumer<T>> funcs;
CompositeConsumer(List<Consumer<T>> funcs) {
this.funcs = funcs;
}
@Override
public void accept(T t) {
for (Consumer<T> func: funcs) {
func.accept(t);
}
}
}
and use it for any number of your functions at once
List<Consumer<String>> funcs = ...
Arrays.asList("a", "b", "c")
.forEach(new CompositeConsumer(funcs));
Upvotes: 0
Reputation: 71
Continuing from Stuart Marks' answer, it is possible to write the lambdas inline but only the first consumer needs to be cast :
Arrays.asList("a", "b", "c").forEach(
((Consumer<String>) s -> System.out.println(s + "1"))
.andThen(s->System.out.println(s + "2"))
.andThen(s -> System.out.println(s + "3"))
);
Upvotes: 6
Reputation: 3300
Chaining with andThen
is indeed the way to go, but we need some syntactic sugar to make it pretty:
public class Consumers {
public static <T> Consumer<T> of(Consumer<T> base) {
return base;
}
}
This allows us to do:
Arrays.asList("a", "b", "c")
.stream()
.forEach(Consumers.of(s -> System.out.println(s + "1"))
.andThen(s -> System.out.println(s + "2"))
.andThen(s -> System.out.println(s + "3")));
Or (shorter):
Arrays.asList("a", "b", "c")
.forEach(Consumers.of(s -> System.out.println(s + "1"))
.andThen(s -> System.out.println(s + "2"))
.andThen(s -> System.out.println(s + "3")));
(since forEach
is available directly on the collection)
Upvotes: 3
Reputation: 132390
The forEach
operation is indeed a terminal operation and is thus not chainable, but it is possible to compose several operations (consumers, in this case) into a single forEach
call. For example:
Consumer<String> c1 = s -> System.out.println(s + "1");
Consumer<String> c2 = s -> System.out.println(s + "2");
Consumer<String> c3 = s -> System.out.println(s + "3");
Arrays.asList("a", "b", "c")
.stream()
.forEach(c1.andThen(c2).andThen(c3));
Unfortunately it doesn't appear possible to write the lambdas inline without a bunch of ugly casting.
Upvotes: 9
Reputation: 691785
Because forEach is a terminal operation. It forces the stream to consume all its elements and call the consumer for each of them. Once that is done, the stream has been consumed, and can't be reused.
A Stream has as many intermediate operations as you want, but can have just one terminal operation.
Upvotes: 13
Reputation: 50044
The methods you are looking for exist and are called Stream::peek and Stream::map. With Stream::peek
, the above code might look as follows.
Arrays
.stream(Girls.toArray())
.peek(Girls::getUp)
.peek(Girls::dressUp)
.filter(/* Top 10 Girls */)
.peek(Gay.getMe()::gotGirl)
...
Upvotes: 28