我的数字从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);
}
}发布于 2015-07-22 16:04:15
我有种强烈的感觉,你没有达到分裂的目的。它不是要复制底层数据,而是提供对一系列数据的访问。请记住,分配器提供只读访问。因此,您应该将原始数组传递给新的分配器,并将其配置为适当的位置和长度,而不是复制数组。
但是,除了复制效率低下之外,逻辑显然是错误的:您将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;
}
}当然,建议在可能的情况下提供一个专门的forEachRemaining实现:
@Override
public void forEachRemaining(Consumer<? super Long> action) {
int pos=currentPosition, end=endPosition;
currentPosition=end;
for(;pos<end; pos++) action.accept(numbers[pos]);
}最后,对于从数组中求和长的任务,首选是Spliterator.OfLong和LongStream,并且已经完成了这项工作,请参阅Arrays.spliterator()和LongStream.sum(),使整个任务与Arrays.stream(numbers).sum()一样简单。
https://stackoverflow.com/questions/31564464
复制相似问题