在Spliterator的.tryAdance()的实现中,是否有任何危险来执行.accept()多个元素?

fge*_*fge 6 java java-8 spliterator

javadoc Spliterator提到:

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

然后我们转到javadoc,tryAdvance()其中说:

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

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

action.accept(arg1);
action.accept(arg2);
return true;
Run Code Online (Sandbox Code Playgroud)

这个项目中,我重写了广泛的第一个分裂器,现在它读取:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

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

请注意,.trySplit()始终返回null; 分裂器具有特征DISTINCT,ORDERED并且NONNULL.

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

Hol*_*ger 8

tryAdvance()应该只消耗一个元素的假设是正确的.但是,这并不意味着您会立即注意到违反合同的行为.当您使用类似操作进行测试时.collect(Collectors.toList()),甚至不太可能发现这样的违规行为,因为大多数消耗所有元素的操作都会forEachRemaining()在spliterator上调用,其default实现记录为:

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

显然,对于那种方法,它没有任何区别.

Stream框架将tryAdvance()在执行延迟操作时调用.因此,当您使用时,.peek(System.out::println).findFirst()您可能会注意到当您的tryAdvance()实现推送多个值时会有所不同.尽管如此,鉴于当前的实现,结果是正确的第一个元素.显然,实现提供的消费者在遇到值后忽略后续值.

这可能与其他实现细节有关,比如"为什么在flatflow()之后的过滤器()在Java流中不是完全'懒惰?".如果流实现本身在某些情况下推送的值超过了必要的值,那么同一实现中的接收端必须准备好处理该情况.

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


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

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