在 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
;拆分器具有 DISTINCT
、ORDERED
和 NONNULL
.
的特征
那么,是否存在一种流用法,由于上面的方法一次消耗所有元素,上面的代码将无法工作?
您关于 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 之外,Spliterator
s 可能还有用例。
好吧,我终于找到了一个与您的 Spliterator
:
中断的操作示例
for(Iterator<?> it=Spliterators.iterator(spliterator); it.hasNext();) {
System.out.println(it.next());
}
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
;拆分器具有 DISTINCT
、ORDERED
和 NONNULL
.
那么,是否存在一种流用法,由于上面的方法一次消耗所有元素,上面的代码将无法工作?
您关于 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 之外,Spliterator
s 可能还有用例。
好吧,我终于找到了一个与您的 Spliterator
:
for(Iterator<?> it=Spliterators.iterator(spliterator); it.hasNext();) {
System.out.println(it.next());
}