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

时间:2017-07-20 16:25:59

标签: java-8 java-stream

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

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

关于API documentation我期望使用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);

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;
    }
}

1 个答案:

答案 0 :(得分:7)

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

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

WHEN Spliteartor是SIZED Spliteartor,流将使用Spliterator#getExactSizeIfKnown来创建数组。

  

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

如果 estimateSize &gt; = IllegalArgumentException

IF 并行运行,Stream#toArray会引发Long.MAX_VALUE - 8

IF 该流是一个顺序流,Stream#toArray将其内部数组容量增加到 estimateSize