2015-03-13 45 views
3

我注意到有位在下列情况下一个奇怪的行为:的Java 8迭代开始流入到迭代导致hasNext()冗余呼叫

迭代器 - >流 - >地图() - >中的iterator() - >迭代

原始迭代器的hasNext()在已经返回false后被称为额外时间。

这是正常的吗?

package com.test.iterators; 

import java.util.Iterator; 
import java.util.Spliterators; 
import java.util.stream.Stream; 
import java.util.stream.StreamSupport; 

public class TestIterator { 

    private static int counter = 2; 

    public static void main(String[] args) { 

     class AdapterIterator implements Iterator<Integer> { 
      boolean active = true; 

      @Override 
      public boolean hasNext() { 
       System.out.println("hasNext() called"); 

       if (!active) { 
        System.out.println("Ignoring duplicate call to hasNext!!!!"); 
        return false; 
       } 

       boolean hasNext = counter >= 0; 
       System.out.println("actually has next:" + active); 

       if (!hasNext) { 
        active = false; 
       } 

       return hasNext; 
      } 

      @Override 
      public Integer next() { 
       System.out.println("next() called"); 
       return counter--; 
      } 
     } 

     Stream<Integer> stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(new AdapterIterator(), 0), false); 
     stream.map(num -> num + 1).iterator().forEachRemaining(num -> { 
      System.out.println(num); 
     }); 
    } 
} 

如果我或者删除地图()或()像数或收集(更换最终itearator()),它的工作原理没有冗余呼叫。

输出

hasNext() called 
actually has next:true 
next() called 
3 
hasNext() called 
actually has next:true 
next() called 
2 
hasNext() called 
actually has next:true 
next() called 
1 
hasNext() called 
actually has next:true 
hasNext() called 
Ignoring duplicate call to hasNext!!!! 
+1

我不会称之为“正常”,但在规范内。 – Holger 2015-03-13 17:48:43

+0

你的意思是因为Iterator.hasNext()必须是幂等的,对吧? – 2015-03-13 21:59:32

+1

对,可能会像调用者喜欢的那样频繁地调用下一个... – Holger 2015-03-16 08:41:06

回答

1

是的,这是正常的。多余的调用发生在StreamSpliterators.AbstractWrappingSpliterator.fillBuffer(),这是从stream.map(num -> num + 1).iterator()返回的迭代器的hasNext()方法中调用的。从JDK 8来源:

/** 
* If the buffer is empty, push elements into the sink chain until 
* the source is empty or cancellation is requested. 
* @return whether there are elements to consume from the buffer 
*/ 
private boolean fillBuffer() { 
    while (buffer.count() == 0) { 
     if (bufferSink.cancellationRequested() || !pusher.getAsBoolean()) { 
      if (finished) 
       return false; 
      else { 
       bufferSink.end(); // might trigger more elements 
       finished = true; 
      } 
     } 
    } 
    return true; 
} 

的调用pusher.getAsBoolean()电话hasNext()原始AdapterIterator实例。如果为true,则将下一个元素添加到bufferSink并返回true,否则返回false。当原始迭代器用完所有项并返回false时,此方法将调用bufferSink.end()并重试填充缓冲区,这会导致多余的hasNext()调用。

在这种情况下,bufferSink.end()有没有效果,第二次尝试,填补了缓冲是不必要的,但作为源注释解释说,它“可能会引发更多的元素”,在另一种情况。这只是一个埋在Java 8流的复杂内部工作中的实现细节。