Spliterator Java 8

Sur*_*ert 2 java-8 spliterator

我有一个1到10,000的数字存储在一个数组中long.按顺序添加它们将得到50,005,000的结果.
我编写了一个Spliterator,如果一个数组的大小超过1000,它将被拆分为另一个数组.这是我的代码.但是当我运行它时,添加的结果远远超过50,005,000.有人能告诉我我的代码有什么问题吗?

非常感谢.

import java.util.Arrays;
import java.util.Optional;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class SumSpliterator implements Spliterator<Long> {

    private final long[] numbers;
    private int currentPosition = 0;

    public SumSpliterator(long[] numbers) {
        super();
        this.numbers = numbers;
    }

    @Override
    public boolean tryAdvance(Consumer<? super Long> action) {
        action.accept(numbers[currentPosition++]);
        return currentPosition < numbers.length;
    }

    @Override
    public long estimateSize() {
        return numbers.length - currentPosition;
    }

    @Override
    public int characteristics() {
        return SUBSIZED;
    }

    @Override
    public Spliterator<Long> trySplit() {
        int currentSize = numbers.length - currentPosition;

        if( currentSize <= 1_000){
            return null;
        }else{
            currentPosition = currentPosition + 1_000;
            return new SumSpliterator(Arrays.copyOfRange(numbers, 1_000, numbers.length));
        }
    }

    public static void main(String[] args) {

        long[] twoThousandNumbers = LongStream.rangeClosed(1, 10_000).toArray();

        Spliterator<Long> spliterator = new SumSpliterator(twoThousandNumbers);
        Stream<Long> stream = StreamSupport.stream(spliterator, false);

        System.out.println( sumValues(stream) );
    }

    private static long sumValues(Stream<Long> stream){
        Optional<Long> optional = stream.reduce( ( t, u) ->  t + u );

        return optional.get() != null ? optional.get() : Long.valueOf(0);
    }

}
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 5

我有强烈的感觉,你没有达到分裂正确的目的.这并不意味着复制基础数据,而只是提供对其中一系列数据的访问.请记住,分裂器提供只读访问权限.因此,您应该将原始数组传递给新的spliterator,并使用适当的位置和长度对其进行配置,而不是复制数组.

但除了复制效率低之外,逻辑显然是错误的:你传递Arrays.copyOfRange(numbers, 1_000, numbers.length)给新的分裂器,所以新的分裂器包含从1000阵列的位置到末尾的元素,并且你将当前分裂器的位置提前1000,所以旧的分裂​​器覆盖元素从currentPosition + 1_000数组到结尾.因此,两个分裂器将覆盖阵列末端的元素,同时,根据之前的值currentPosition,开头的元素可能根本不会被覆盖.所以,当你想提前currentPosition通过1_000跳过的范围由表达Arrays.copyOfRange(numbers, currentPosition, 1_000)相反,指的是currentPosition 推进.

还应该注意的是,分裂器应该尝试分离平衡,即,如果已知大小,则在中间.因此,拆分千元素并不是阵列的正确策略.

此外,你的tryAdvance方法是错误的.它不应该调用消费者之后进行测试,之前false如果没有更多的元素则返回,这也意味着消费者没有被调用.

总而言之,实现可能看起来像

public class MyArraySpliterator implements Spliterator<Long> {

    private final long[] numbers;
    private int currentPosition, endPosition;

    public MyArraySpliterator(long[] numbers) {
        this(numbers, 0, numbers.length);
    }
    public MyArraySpliterator(long[] numbers, int start, int end) {
        this.numbers = numbers;
        currentPosition=start;
        endPosition=end;
    }

    @Override
    public boolean tryAdvance(Consumer<? super Long> action) {
        if(currentPosition < endPosition) {
            action.accept(numbers[currentPosition++]);
            return true;
        }
        return false;
    }

    @Override
    public long estimateSize() {
        return endPosition - currentPosition;
    }

    @Override
    public int characteristics() {
        return ORDERED|NONNULL|SIZED|SUBSIZED;
    }

    @Override
    public Spliterator<Long> trySplit() {
        if(estimateSize()<=1000) return null;
        int middle = (endPosition + currentPosition)>>>1;
        MyArraySpliterator prefix
                           = new MyArraySpliterator(numbers, currentPosition, middle);
        currentPosition=middle;
        return prefix;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,当然,建议尽可能提供专门的forEachRemaining实施:

@Override
public void forEachRemaining(Consumer<? super Long> action) {
    int pos=currentPosition, end=endPosition;
    currentPosition=end;
    for(;pos<end; pos++) action.accept(numbers[pos]);
}
Run Code Online (Sandbox Code Playgroud)

最后一点,对于总结多头从数组,任务Spliterator.OfLongLongStream优先和工作已经完成,看Arrays.spliterator()LongStream.sum(),使得整个任务就这么简单Arrays.stream(numbers).sum().