在Spliterator的.tryAdance()实现中,是否存在操作.accept()多个元素的危险吗?

时间:2016-04-28 17:34:15

标签: java java-8 spliterator

Spliterator的javadoc提到:

  

Spliterator可以单独遍历元素(tryAdvance())或按顺序遍历元素(forEachRemaining())。

然后我们转到javadoc of tryAdvance()说:

  

如果存在剩余元素,则对其执行给定操作,返回true; else返回false。

也许我在某个地方误读了,但对我来说似乎只要剩下一个元素或更多Consumer作为一个参数应该只有每一个.accept()返回true之前的一个参数,如果我有两个参数可以立即使用,那么我就不能:

action.accept(arg1);
action.accept(arg2);
return true;

this project中,我重写了广泛的第一个分裂器,现在它写着:

// deque is a Deque<Iterator<T>>

@Override
public boolean tryAdvance(final Consumer<? super T> action)
{
    Iterator<T> iterator;
    T element;

    while (!deque.isEmpty()) {
        iterator = deque.removeFirst();
        while (iterator.hasNext()) {
            element = iterator.next();
            deque.add(fn.apply(element));
            action.accept(element);
        }
    }
    return false;
}

简而言之,我让action接受所有参数,然后返回false ...而测试虽然很简单,但仍然成功(link)。

请注意,.trySplit()始终返回null;分词符具有特征DISTINCTORDEREDNONNULL

那么,是否有一个流使用,由于上面的方法一次性消耗所有元素,上面的代码将无法工作?

1 个答案:

答案 0 :(得分:8)

您认为tryAdvance()只应使用一个元素是正确的。但是,这并不意味着您会立即注意到违反合同的行为。当您使用.collect(Collectors.toList())等操作进行测试时,甚至不太可能发现此类违规行为,因为大多数使用所有元素的操作都会在default implementation is documented as:<的分裂器上调用forEachRemaining() / p>

  

默认实现重复调用tryAdvance(java.util.function.Consumer),直到返回false。

显然, 方法没有区别。

Stream框架将在执行延迟操作时调用tryAdvance()。因此,当您使用.peek(System.out::println).findFirst()时,如果tryAdvance()实施推送多个值,您可能会发现存在差异。仍然,给出了当前的实现,结果是正确的第一个元素。显然,实现提供的消费者在遇到值后会忽略后续值。

这可能与“Why filter() after flatMap() is ‘not completely’ lazy in Java streams?”中讨论的其他实现细节相关联。如果流实现本身在某些情况下推送的值超过了必要的值,那么同一实现中的接收端必须准备好处理这种情况。

但必须强调的是,这是一个特定Stream API实现的行为。不同的实现或下一个Java版本可能依赖于tryAdvance的正确实现。此外,除了Streams之外,Spliterator可能还有用例。

好吧,我终于找到了一个与你的Spliterator打破的操作示例:

for(Iterator<?> it=Spliterators.iterator(spliterator); it.hasNext();) {
    System.out.println(it.next());
}