peek().iterator().hasNext() pre-consumes elements?

Brian Goetz brian.goetz at
Wed Feb 20 08:38:13 PST 2013

Here's the current spec for this method -- does this help?

      * Produce a {@code Stream} containing the elements of this stream, 
and also provide elements
      * to the specified {@link Consumer} as elements are consumed from 
the resulting stream.  This is
      * an <a href="package-summary.html#StreamOps">intermediate 
      * {@apiNote}
      * This method exists mainly to support debugging, where you want 
to see the elements as they flow past a certain
      * point in a pipeline:
      * <pre>
      *         .filter(filteringFunction)
      *         .peek(e -> {System.out.println("Filtered value: " + e); });
      *         .map(mappingFunction)
      *         .peek(e -> {System.out.println("Mapped value: " + e); });
      *         .collect(Collectors.intoList());
      * </pre>
      * <p>For parallel stream pipelines, the {@code Consumer} may be 
called at whatever time and in whatever thread
      * the element is made available by the upstream operation.  If the 
{@code Consumer} modifies shared state,
      * it is responsible for providing the required synchronization.
      * @param consumer The {@code Consumer} to receive the elements

On 2/20/2013 10:02 AM, Georgiy Rakov wrote:
> Hello again,
> it has just come into my mind that it could be quite more major issue
> than I wrote in my previous letter.
> So the case a bit rewritten:
>     Stream s1 = Arrays.asList(1, 2, 3).stream();
>     Stream s2 = s1.peek(System.err::println);
>     s2.iterator().hasNext();
> The spec says:
>     Produce a Stream containing the elements of this stream, and also
>     provide elements to the specified Consumer as elements are *passed
>     through*.
> So the core question is what does "passed through" mean?From the first
> glance I would say it means *consuming elements from **stream returned
> ****by peek()* (not from stream which peek() is applied to). If this
> interpretation is right then I could suppose it's a bug because the
> element from s2 has not been consumed yet (next() is not called just
> hasNext() has been called).
> Could you please confirm if such reasoning is right and it's really a bug.
> Thanks, Georgiy.
> On 12.02.2013 23:01, Remi Forax wrote:
>> On 02/12/2013 07:16 PM, Brian Goetz wrote:
>>> The answer here is complicated, but in general, calling hasNext may well
>>> require consuming an element -- there's often no way to know whether a
>>> source would produce an element without asking it to do so.  So it is a
>>> common practice in implementing iterators to do this (one of many
>>> reasons why we did not build Streams on Iterator.)
>>> Because the elements are coming from an array, it might be possible to
>>> know simply based on how many elements have gone by that the stream is
>>> not yet exhausted.  But in the general case (such as when the stream
>>> source is an IO channel), it is not possible to know without actually
>>> consuming and buffering some input.  So I would put this in the category
>>> of "acceptable" behavior.  We might someday do some work to take
>>> advantage of the fact that the source has the SIZED characteristic and
>>> the pipeline stages are size-preserving to make this case behave
>>> "better", but that would be an implementation quality issue, not a spec
>>> issue.  The behavior you observe is allowable by the spec.
>> while I a stream may have to do some buffering, peek should always be
>> transparent and an iterator on an array doesn't need any buffering but I
>> agree that this is an implementation issue.
>> Rémi
>>> On 2/12/2013 12:53 PM, Dmitry Bessonov wrote:
>>>> Hello,
>>>> The following line prints out the first element, "1"
>>>>       Arrays.asList(1, 2,
>>>> 3).stream().peek(System.err::println).iterator().hasNext()
>>>> Is it really an expected behavior?
>>>> -Dmitry

More information about the lambda-dev mailing list