在 Spliterator 的 .tryAdance() 的实现中使操作 .accept() 超过一个元素是否有任何危险?

Is there any danger in making the action .accept() more than one element in an implementation of Spliterator's .tryAdance()?

Spliterator 的 javadoc 提到:

A Spliterator may traverse elements individually (tryAdvance()) or sequentially in bulk (forEachRemaining()).

然后我们转到 javadoc of tryAdvance() 上面写着:

If a remaining element exists, performs the given action on it, returning true; else returns false.

也许我在某处误读了,但对我来说似乎只要有一个元素,或更多,剩下的,Consumer作为参数应该只.accept() returning 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 接受所有参数,然后 return false... 测试虽然很简单,但仍然成功 (link)。

注意.trySplit()总是returns null;拆分器具有 DISTINCTORDEREDNONNULL.

的特征

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

您关于 tryAdvance() 应该只消耗一个元素的假设是正确的。但是,这并不意味着您会立即注意到违反合同的情况。当您使用 .collect(Collectors.toList()) 之类的操作进行测试时,甚至 不太可能 发现这种违规行为,因为大多数消耗所有元素的操作都会在拆分器上调用 forEachRemaining(),其 default implementation is documented as:

The default implementation repeatedly invokes tryAdvance(java.util.function.Consumer) until it returns false.

显然,那个方法没有区别。

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

这可能与 中讨论的其他实施细节有关。如果流实现本身在某些情况下推送了比必要更多的值,则同一实现中的接收端必须准备好处理这种情况。

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


好吧,我终于找到了一个与您的 Spliterator:

中断的操作示例
for(Iterator<?> it=Spliterators.iterator(spliterator); it.hasNext();) {
    System.out.println(it.next());
}