1

Similar to this question: Java Streams — How to perform an intermediate function every nth item

But this time, always do something intermediate on the last terminal value retrieved. Something akin to this:

IntStream.rangeClosed(1, 26)
         .filter(it -> isPrime(it))
         .peek(every(5, System.out::println))
         .onFinalValue( it -> System.out.println("Last value is: " + it))
         .forEach( it -> {});

That way it should print the values every 5th one, but also show the very last one.

You may, or may not, know the length of the original stream. So it may be hot (live audio, tv video feed) or cold (pre-recorded audio, or other forward/backward seekable source).

Community
  • 1
  • 1
The Coordinator
  • 13,007
  • 11
  • 44
  • 73
  • Can you know beforehand the length of your stream? – Alexey Soshin Oct 15 '16 at 09:07
  • Sometimes, but needs to work even in the event that the length of the stream feed is arbitrary. – The Coordinator Oct 15 '16 at 09:10
  • 2
    It seems you're trying to turn streams into a reactive framework, and, big surprise, it's not doing what you want -- because it's not a reactive framework. Try RxJava. – Brian Goetz Oct 15 '16 at 16:11
  • True enough. But I am going with @alexey-soshin answer below, He really does find a neat solution using Stream.reduce. I am learning RxJava right now and I think this is a very good way to re-look at what the regular streams are capable of with the correct compositions. – The Coordinator Oct 15 '16 at 16:18
  • 3
    You're better off using streams to produce the values, and then asking the stream for an `iterator()`, and then do your stateful sequential stuff with that. Yes, you don't get to chain things all the way down, but then you are actually writing code that looks like the problem you're trying to solve. The reduce trick obfuscates the meaning of what you're trying to do -- all for the extremely dubious benefit of "look, it's one big chained expression." – Brian Goetz Oct 15 '16 at 17:02
  • I tend to agree. A lot of the Stream conversions I am seeing now obfuscate the nature of what the problem/solution is. Everything can be a Stream, even the exception becomes a Stream (or raging undecipherable rapids) :) – The Coordinator Oct 15 '16 at 19:45

2 Answers2

2

Try this.

int[] last = {0};
IntStream.rangeClosed(1, 26)
    .filter(it -> isPrime(it))
    .peek(every(5, System.out::println))
    .peek(it -> last[0] = it)
    .count();
System.out.println("Last value is: " + last[0]);
  • Note that this will only work with sequential streams. – René Link Oct 15 '16 at 09:48
  • 1
    When you discover things like "will only work with sequential streams", this should be your clue to "I am abusing my tools and will probably get hurt." – Brian Goetz Oct 15 '16 at 16:09
  • 3
    Note too that the peek targets are not guaranteed to be called here; if `count()` can determine the size without traversing the elements, it can and will do so. Again: you're abusing your tools, and someone is going to get hurt. – Brian Goetz Oct 15 '16 at 16:12
2

Since count() is a terminating operation, you'll need to switch your logic a bit:

    int res = IntStream.rangeClosed(1, 26)
            .filter(it -> countIfPrime(it))
            .peek(every(5, System.out::println))
            .reduce(0, (left, right) -> right);

    System.out.println("Final " + res);

In countIfPrime() you should also increase the count

Alexey Soshin
  • 16,718
  • 2
  • 31
  • 40
  • .. and count() terminating operator cannot be used on some streams that short-circuits to a known value. Just learned that somewhere else! I never thought of reduce! – The Coordinator Oct 15 '16 at 09:51
  • 3
    Forget about `peek()` and fake terminals; you're pushing in the wrong direction. Instead, ask the stream for an `iterator()`, and traverse that with the sequential, stateful logic you want to apply, rather than trying to trick streams into doing the sequential stateful logic for you. – Brian Goetz Oct 15 '16 at 16:15