3

I was learning Stream.limit() says:

Returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length.

This can be understood from:

Stream.of(1,2,3,4,5,6,7,8)
    .limit(3)
    .forEach(i -> {
        System.out.print(i + ",");
    });  //prints: 1,2,3,

However when used with other stream methods, it has an effect of processing elements in batch:

Stream.of(1,2,3,4,5,6,7)
    .filter(i -> {
        System.out.println("Filtering "  + i + ": " + (i%2 == 0));
        return i%2 == 0;
    })
    .map(i-> {
        System.out.println("Mapping " + i + " to " + i*i);
        return i*i;
    })
    .limit(2)
    .forEach(i -> System.out.println("---> " + i));

Prints:

Filtering 1: false
Filtering 2: true
Mapping 2 to 4
---> 4
Filtering 3: false
Filtering 4: true
Mapping 4 to 16
---> 16

Here, you can see elements are processed in batch of 2.

I have following doubts:

  1. Why it did not processed only first two elements 1 and 2? That is, why the output is not just:

     Filtering 1: false
     Filtering 2: true
     Mapping 2 to 4
     ---> 4
    
  2. Why it did not processed last four elements 5,6,7 and 8 and printed following?:

     Filtering 5: false
     Filtering 6: true
     Mapping 6 to 36
     ---> 36
     Filtering 7: false
     Filtering 8: true
     Mapping 8 to 64
     ---> 64
    
Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
MsA
  • 2,599
  • 3
  • 22
  • 47
  • They're not processed in batches of two. They're processed one by one, but since the first and third ones are rejected by the filter, they're not mapped, i.e. they're not passed to the next operation of the pipeline which is the mapping function. – JB Nizet Oct 25 '18 at 09:44
  • 2
    It depends where your limit instruction is placed. – eric.v Oct 25 '18 at 09:47
  • @eric.v like? any other example? – MsA Oct 25 '18 at 10:07
  • I recommend reading [this answer](https://stackoverflow.com/a/35157305/2711488), then imagine you insert a “let pass at most *n* elements” stage into the pipeline, once at the beginning and once at the end… – Holger Oct 25 '18 at 16:06

1 Answers1

5
  1. Both 1 and 2 were processed. 1 is odd so the filter removes it from the stream pipeline. 2 is even and went through entire stream pipeline resulting in 4 being printed out in forEach.

  2. Streams are lazy in nature. Since you used limit(2) only two elements will make it through the limit pipeline step. 2 and 4 went through entire stream pipeline resulting in 4 and 16 being printed out in forEach.

limit() is a short-circuiting stateful intermediate operation, as per docs:

Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.

...

Further, some operations are deemed short-circuiting operations. An intermediate operation is short-circuiting if, when presented with infinite input, it may produce a finite stream as a result. A terminal operation is short-circuiting if, when presented with infinite input, it may terminate in finite time. Having a short-circuiting operation in the pipeline is a necessary, but not sufficient, condition for the processing of an infinite stream to terminate normally in finite time.

Community
  • 1
  • 1
Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • Why 6 and 8 didnt make through limit? In fact it seems that 5,6,7 and 8 werent even went through `filter()` (for example, there is no `Filtering 5: false` in the output), right? Or am I missing anything? – MsA Oct 25 '18 at 09:57
  • @anlr because `limit()` is a short-circuiting operation – Karol Dowbecki Oct 25 '18 at 10:02
  • Not exactly getting what does it mean by short circuit, need to read more online. But a quick question, does it mean that it terminates pipeline execution after arbitrary time? – MsA Oct 25 '18 at 10:07
  • @anir Yes. An example of short-circuiting will be `&&` operator. In `(a == 1 && b == 1)` if left condition is false than right condition is not evaluated. – Karol Dowbecki Oct 25 '18 at 10:08
  • But then is there any way to control how many elements are short circuited / not processed? Or is it like that first half of the elements are processed and rest are left unprocessed? I mean are thereany more details to it? any good link? – MsA Oct 25 '18 at 10:09
  • 2
    @anir, Java Stream API works in "pull-way", not in "push-way". It means that it evaluates last operations first, and and first operation last in chain. And because this is 'pull-way', there is no any way to 'pull' more elements than defined on 'limit' stage. – Ivan Zelenskyy Oct 25 '18 at 11:39
  • What do you mean by "there is no any way to 'pull' more elements than defined on 'limit' stage". Here `2` is passed to `limit()`, but 4 elements (from 1 to 4) are processed and rest are left unprocessed (from 5 to 8). – MsA Oct 25 '18 at 13:00
  • @anir I don’t know your cultural background, but the Stream API was made by people reading from left to right. When reading from left to right, the difference between `source.limit(2).filter(…).forEach(…)` and `source.filter(…).limit(2).forEach(…)` is obvious. The first variant means “accept at most two elements, *then* remove all non-matching elements from those two (or less)”. The second means “accept those matching the filter predicate *then* keep at most two of those matches”. The processing is irrelevant, only the *result* matters, which is, which values were printed by the consumer. – Holger Oct 25 '18 at 15:05