具有未知大小的AbstractSpliterator实现抛出OutOfMemoryError:Java堆空间

Mig*_*boa 5 java-8 java-stream

java.lang.OutOfMemoryError: Java heap space当我使用AbstractSpliterator实现时,我有一个报告未知大小的实现.

在这种情况下,我定义了一个类StreamCollapse,它AbstractSpliteratortryAdvance()实现中扩展和合并一系列相邻元素.它的构造函数将超级构造函数调用为super(Long.MAX_VALUE, source.characteristics()).

关于API文档,我期望使用a Long.MAX_VALUE表示未知大小.但是,它似乎正在尝试分配具有该大小的内存.

为什么要分配那个空间?我应该使用什么价值估算大小?

这是一个示例测试:

Stream<Integer> nrs = Stream.of(3, 3, 5, 5, 3, 3, 3, 4, 4, 4 ,5 , 5);
Integer [] expected = {3, 5, 3, 4, 5};
Object[] actual = collapse(nrs).toArray();
assertEquals(actual, expected);
Run Code Online (Sandbox Code Playgroud)

collapse()方法实现:

static <T> Stream<T> collapse(Stream<T> source) {
    return StreamSupport.stream(
            new StreamCollapse<T>(source.spliterator()), false);
}

class StreamCollapse<T> extends AbstractSpliterator<T> implements Consumer<T> {

    private final Spliterator<T> source;
    private T curr = null;

    StreamCollapse(Spliterator<T> source) {
        super(Long.MAX_VALUE, source.characteristics());
        this.source = source;
    }

    @Override
    public boolean tryAdvance(Consumer<? super T> action) {
        T prev = curr;
        boolean hasNext;
        while ((hasNext = source.tryAdvance(this)) && curr.equals(prev)) { }
        if(hasNext) action.accept(curr);
        return hasNext;
    }

    @Override
    public void accept(T item) {
        curr = item;
    }
}
Run Code Online (Sandbox Code Playgroud)

hol*_*ava 7

您应该从合成的分裂器中删除特征,例如:

// an unknown spliterator shouldn't having SIZED | SUBSIZED  characteristics
//                                             v  
super(Long.MAX_VALUE, source.characteristics() & (~(SIZED | SUBSIZED)));
Run Code Online (Sandbox Code Playgroud)

Spliteartor是SIZED Spliteartor时,Spliterator#getExactSizeIfKnown将由stream用于创建数组.

表示从遍历分裂estimateSize()之前返回的值表示有限大小的特征值,在没有结构源修改的情况下,表示完整遍历将遇到的元素数量的精确计数.

如果流并行运行,则Stream#toArray将抛出一个IllegalArgumentException如果estimateSize > = Long.MAX_VALUE - 8.

如果流是顺序流,则Stream#toArray将其内部数组容量增加到estimateSize.

  • 这是正确答案,分裂者*必须*清除"SIZED"和"SUBSIZED"特征.另外,使用源spliterator的`estimateSize()`作为过滤分裂器的估计大小将是一个好方法.毕竟,实际大小将介于零和源大小之间.这就是`filter`也是如此,当前的实现将比完全未知的大小更好地处理. (3认同)