wener
wener

Reputation: 7750

Why not chain java.util.stream.Stream#forEach

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

Answers (6)

Andriy Kryvtsun
Andriy Kryvtsun

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

DataFusions
DataFusions

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

marthursson
marthursson

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 forEachis available directly on the collection)

Upvotes: 3

Stuart Marks
Stuart Marks

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

JB Nizet
JB Nizet

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

nosid
nosid

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

Related Questions