我试图了解它是如何Spliterator工作的,以及如何设计分裂器.我认识到这trySplit()可能是更重要的方法之一Spliterator,但是当我看到一些第三方Spliterator实现时,有时我会看到他们的分裂器trySplit()无条件地返回null .
问题:
Spliterator无条件返回null 之间有区别吗?看起来这样的分裂者似乎击败了分裂.trySplit(),但是有一个无条件返回null的分裂器的合法用例吗?我想使用a Stream并行处理未知数量(文件数量在预先未知的情况下)的异类远程存储JSON文件集。文件的大小可以相差很大,从每个文件1个JSON记录到其他文件中的100,000个记录。甲JSON记录在这种情况下是指表示为文件中的一条线的自包含JSON对象。
我真的很想为此使用Streams,所以我实现了这一点Spliterator:
public abstract class JsonStreamSpliterator<METADATA, RECORD> extends AbstractSpliterator<RECORD> {
abstract protected JsonStreamSupport<METADATA> openInputStream(String path);
abstract protected RECORD parse(METADATA metadata, Map<String, Object> json);
private static final int ADDITIONAL_CHARACTERISTICS = Spliterator.IMMUTABLE | Spliterator.DISTINCT | Spliterator.NONNULL;
private static final int MAX_BUFFER = 100;
private final Iterator<String> paths;
private JsonStreamSupport<METADATA> reader = null;
public JsonStreamSpliterator(Iterator<String> paths) {
this(Long.MAX_VALUE, ADDITIONAL_CHARACTERISTICS, paths);
}
private JsonStreamSpliterator(long est, int additionalCharacteristics, Iterator<String> paths) {
super(est, additionalCharacteristics);
this.paths = paths;
}
private JsonStreamSpliterator(long …Run Code Online (Sandbox Code Playgroud) 我正在使用流分裂器直接用于我正在编写的库中的低级操作.最近,当我使用流分裂器和交错tryAdvance/trySplit调用时,我发现了非常奇怪的行为.这是一个简单的代码,演示了这个问题:
import java.util.Arrays;
import java.util.Spliterator;
public class SpliteratorBug {
public static void main(String[] args) {
Integer[][] input = { { 1 }, { 2, 3 }, { 4, 5, 6 }, { 7, 8 }, { 9 } };
Spliterator<Integer> spliterator = Arrays.stream(input).parallel()
.flatMap(Arrays::stream).spliterator();
spliterator.trySplit();
spliterator.tryAdvance(s -> {});
spliterator.trySplit();
spliterator.forEachRemaining(System.out::println);
}
}
Run Code Online (Sandbox Code Playgroud)
输出是
5
6
9
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,后平映射我应该得到连续编号的有序流1来9.我将分裂器分开一次,所以它应该跳到一些中间位置.接下来我从中消耗一个元素并将其拆分一次.之后我打印所有剩余的元素.我希望我将从流尾部有几个连续的元素(可能是零元素,它也会很好).然而我所得到的是5和6,然后突然跳出来9.
我知道目前在JDK分裂器中并没有这样使用:它们总是在遍历之前分裂.但是官方文档没有明确禁止调用trySplit后tryAdvance.
当我使用直接从集合,数组,生成的源等创建的spliterator时,从未观察到这个问题.只有当spliterator是从具有中间体的并行流创建时才会观察到flatMap.
所以问题是:我是否遇到了这个错误,或者明确禁止某个地方以这种方式使用分裂器?
我正在实现一个Spliterator通过trySplit()返回明确限制并行化的方法null.实施estimateSize()是否会为此分裂器生成的流提供任何性能改进?或者估计的大小仅对并行化有用?
编辑:澄清一下,我特别询问估计的大小.换句话说,我的分裂者没有这个SIZED特征.
我读了一些关于如何创建有限的问题Stream(
在Java中有限生成的流 - 如何创建一个?,流如何停止?).
答案建议实施一个Spliterator.在Spliterator将实现逻辑如何和元件,以提供为下一个(tryAdvance).但也有其他两个非默认的方法trySplit和estimateSize()我将不得不执行.
JavaDoc Spliterator说:
用于遍历和分区源元素的对象.a覆盖的元素源
Spliterator可以是例如阵列,aCollection,IO通道或生成器函数....Spliterator除了顺序遍历之外,API还支持有效的并行遍历,支持分解以及单元素迭代....
另一方面,我可以实现逻辑如何前进到a周围的下一个元素Stream.Builder并绕过a Spliterator.在每次前进时我都会打电话accept或者add最后打电话build.所以它看起来很简单.
JavaDoc说什么?
一个可变的建设者
Stream.这允许Stream通过单独生成元素并将它们添加到Builder(无需使用ArrayList作为临时缓冲区的复制开销)来创建.
使用StreamSupport.stream我可以用a Spliterator来获取Stream.并且Builder还将提供一个Stream.
什么时候应该/我可以使用Stream.Builder?
只有当a Spliterator不会更高效时(例如因为无法对源进行分区并且无法估计其大小)?
根据文件Spliterator#getComparator,它说
如果此Spliterator的源是
SORTEDaComparator,则返回Comparator.如果源是SORTED自然顺序,则返回null.否则,如果源不是SORTED,则抛出IllegalStateException.实施要求:
始终抛出默认实现
IllegalStateException.返回:a
Comparator,或者null元素是按自然顺序排序的.抛出:
IllegalStateException- 如果分裂器没有报告特征SORTED.
所以在运行这段代码时
Spliterator<Integer> spliterator = Stream.of(1, 2, 3).sorted().spliterator();
System.out.println((spliterator.characteristics() & Spliterator.SORTED) == Spliterator.SORTED);
System.out.println(spliterator.getComparator());
Run Code Online (Sandbox Code Playgroud)
我明白了:
true
null
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.现在这样做:
Spliterator<Integer> spliterator = Stream.of(1, 2, 3).sorted(Comparator.naturalOrder()).spliterator();
System.out.println((spliterator.characteristics() & Spliterator.SORTED) == Spliterator.SORTED);
System.out.println(spliterator.getComparator());
Run Code Online (Sandbox Code Playgroud)
它输出false并抛出异常:
Exception in thread "main" java.lang.IllegalStateException
at java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.getComparator(StreamSpliterators.java:259)
at SpliteratorTest.main(SpliteratorTest.java:10)
Run Code Online (Sandbox Code Playgroud)
为什么它输出false并抛出异常?
根据文件,它不应该给我Comparator提供给我的sorted()吗?
(这也发生在reverseOrder() …
我有一个方法返回从自定义spliterator生成的流; 分裂器不是安全的.由于分裂器不是踏板安全的,并且它保持状态,我想防止它并行运行.有没有办法阻止返回的流并行运行?
我无法找到任何执行此操作的文档或示例.我确实sequential()在BaseStream类上找到了一个方法,但这似乎并没有阻止用户调用parallel()以获得并行流.
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 …
https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html
SIZED特征值表示在遍历或拆分之前从estimateSize()返回的值表示有限大小,在没有结构源修改的情况下,表示完整遍历将遇到的元素数量的精确计数.
SUBSIZED特征值表示由trySplit()生成的所有Spliterator都将是SIZED和SUBSIZED.
我今天读了一下spliterators并实现了一个使用Spliterators.spliteratorUnknownSize(iterator(), Spliterator.NONNULL).根据spliteratorUnknownSize()文件
[结果]分裂器不是后期绑定的
作为分裂者的新手,我想知道为什么会这样.如果我确保iterator()后期绑定,那么得到的分裂器也应该是,不是吗? spliteratorUnknownSize()仅仅创建一个IteratorSpliterator不与元素源绑定的东西.
也就是说,我很想理解为什么得到的分裂器没有后期绑定.谢谢.