Routing Java objects using conditional consumers
Again, I strongly recommend you stick to the methods and systems already provided in the Streams API. In this case, the ability to observe-without-changing the stream is given with the Stream.peek() method and the ability to conditionally remove a value from the stream is given with the Stream.filter() method. Using these two methods with a simple helper class, you can get the functionality you desire. Consider your existing "use case":


Predicate&lt;Integer&gt; p1 = (Integer i) -&gt; {
    System.out.println("call p1");
    return i &lt; 5;
};
Predicate&lt;Integer&gt; p2 = (Integer i) -&gt; {
    System.out.println("call p2");
    return i &gt; 5;
};
Consumer&lt;Integer&gt; c1 = i -&gt; System.out.println("less");
Consumer&lt;Integer&gt; c2 = i -&gt; System.out.println("more");

Router&lt;Integer&gt; routerMatchAll = RouterMatchAll.of(p1, c1)
        .chain(p2, c2);
IntStream.range(0, 10).boxed().forEach(routerMatchAll.toConsumer());
System.out.println("&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;");
Router&lt;Integer&gt; routerMatchOnce = RouterMatchOnce.of(p1, c1).chain(p2,
        c2);
IntStream.range(0, 10).boxed().forEach(routerMatchOnce.toConsumer());



How about a system where you would implement the first stream:


Router&lt;Integer&gt; routerMatchAll = RouterMatchAll.of(p1, c1)
        .chain(p2, c2);
IntStream.range(0, 10).boxed().forEach(routerMatchAll.toConsumer());



as:

    IntStream.range(0, 10).boxed()
        .peek(new ConditionalTee&lt;&gt;(p1, c1))
        .forEach(new ConditionalTee&lt;&gt;(p2, c2));


Note how you use peek() to ensure all values pass through to the forEach()?

The second implementation is currently:


Router&lt;Integer&gt; routerMatchOnce = RouterMatchOnce.of(p1, c1).chain(p2,
        c2);
IntStream.range(0, 10).boxed().forEach(routerMatchOnce.toConsumer());



but I would instead suggest:

    IntStream.range(0, 10).boxed()
        .filter(new ConditionalTee&lt;&gt;(p1, c1))
        .forEach(new ConditionalTee&lt;&gt;(p2, c2));


Note how we call filter() instead of peek()?

Now, what is ConditionalTee? It is a simple class that takes a predicate, and a Consumer, and it calls the consumer with the value, if the value passes, and it returns false if the predicate passes (which means the value will be filtered in a filter call). It is more comments than code, and it's really simple:

public class ConditionalTee&lt;T&gt; implements Predicate&lt;T&gt;, Consumer&lt;T&gt; {

    private final Predicate&lt;? super T&gt; predicate;
    private final Consumer&lt;? super T&gt; consumer;

    public ConditionalTee(
          Predicate&lt;? super T&gt; condition, Consumer&lt;? super T&gt; consumer) {
        this.predicate = condition;
        this.consumer = consumer;
    }

    @Override
    public void accept(T t) {
        test(t);
    }

    @Override
    public boolean test(T t) {
        // NOTE: inverted boolean.
        // this will be called from a Stream.filter()
        // need to return true if the value should stay on the stream
        // false if it should be removed from the stream.
        // We want to keep it on the stream if we are not siphoning it off
        //     (return true).
        // We want to remove from the stream if we are siphoning the value off
        //     (return false).
        if (!predicate.test(t)) {
            return true;
        }
        consumer.accept(t);
        return false;
    }
}


See it all running here, in ideone.