在以下场景中,我注意到了一些奇怪的行为:

迭代器 -> 流 -> map() -> 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); 
        }); 
    } 
} 

如果我删除 map() 或用类似 count() 或 collect() 之类的东西替换最终的 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!!!! 

请您参考如下方法:

是的,这是正常的。冗余调用发生在 StreamSpliterators.AbstractWrappingSpliterator.fillBuffer() ,从 hasNext() 调用stream.map(num -> num + 1).iterator() 返回的迭代器的方法.来自 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实例。如果为真,它将下一个元素添加到 bufferSink并返回真,否则返回假。当原始迭代器用完项目并返回 false 时,此方法调用 bufferSink.end()并重试填充缓冲区,这导致了多余的 hasNext()称呼。

在这种情况下, bufferSink.end()没有效果,第二次尝试填充缓冲区是不必要的,但正如源评论所解释的那样,它在另一种情况下“可能会触发更多元素”。这只是一个深埋在 Java 8 流复杂内部工作中的实现细节。


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!