当前位置: 首页 > 知识库问答 >
问题:

Java 8

梁丘霖
2023-03-14

我在long的数组中存储了一个从1到10,000的数字。当将它们按顺序相加时,会得到50,005,000的结果。
我编写了一个拆分器,如果一个数组的大小大于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);
    }

}

共有1个答案

蔡辰钊
2023-03-14

我强烈地感觉到你没有达到分裂的目的。它不是要复制底层数据,而是提供对其范围的访问。请记住,拆分器提供只读访问。所以您应该将原始数组传递给新的拆分器,并将其配置为适当的位置和长度,而不是复制数组。

但是除了复制效率低之外,逻辑显然是错误的:您将arrays.copyofrange(numbers,1_000,numbers.length)传递给新的拆分器,因此新的拆分器包含从位置1000到数组末尾的元素,而您将当前拆分器的位置提前1000,因此旧的拆分器覆盖从currentPosition+1_000到数组末尾的元素。因此,两个拆分器都将覆盖数组末尾的元素,而同时,根据currentposition的前一个值,开头的元素可能根本不会覆盖。因此,当您希望将currentPosition提前1_000时,跳过的范围将由Arrays.copyofrange(numbers,currentPosition,1_000)表示,而是在提前之前引用currentPosition

还应该注意的是,分裂器应该尝试平衡分裂,也就是说,如果大小已知,在中间分裂。因此拆分数千个元素不是一个数组的正确策略。

此外,您的tryAdvance方法是错误的。它不应该在调用使用者之后而在调用之前进行测试,如果没有更多的元素,则html" target="_blank">返回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;
    }
}

但当然,建议在可能的情况下提供专门的foreachlething实现:

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

最后要说明的是,对于从数组中求和长的任务,最好使用spliterator.oflonglongstream并且已经完成了这项工作,请参阅arrays.spliterator()longstream.sum(),使整个任务与arrays.stream(numbers).sum()一样简单。

 类似资料:
  • 问题内容: 我只是想知道:有了Java 8,并有可能在接口中添加实现(有点像Scala特质),是否有可能像在Scala中那样实现蛋糕模式? 如果是,有人可以提供代码段吗? 问题答案: 从其他答案中得到启发,我想到了以下(粗糙的)类层次结构,该层次结构类似于Scala中的蛋糕模式: 以上代码自2013年1月9日起在Java 8上编译。 因此,可以的Java 8做cake- 喜欢 的图案? 是。 它像

  • 问题内容: Java 8中有什么方法可以将一个元素分组而不收集它们?我希望结果再次出现。因为我必须处理大量数据甚至是无限流,所以我无法先收集数据并再次流处理结果。 所有需要分组的元素在第一流中都是连续的。因此,我喜欢使流评估保持懒惰。 问题答案: 无法使用标准Stream API做到这一点。通常,您无法执行此操作,因为将来总是有可能出现属于任何已创建组的新项目,因此,在处理所有输入之前,您无法将组

  • 问题内容: 拥有具有默认方法的接口的动态代理,如何调用默认方法?通过使用类似的方法,您可以得到名为的代理调用处理程序(这在某种程度上是正确的,因为您没有为此接口实现的类)。 我有一个使用ASM来创建实现接口的类并将此类调用委派给此类实例的解决方法。但这不是一个好的解决方案,特别是如果默认方法调用其他接口方法(您将获得委托人乒乓球)。JLS对此问题出人意料地保持沉默… 这里是一个小代码示例: 问题答

  • 问题内容: 给定以下变量 我想使用以下代码将占位符$ {name}替换为值“ Joe”(不起作用) 但是,如果我采用“旧式”方式,则一切都将正常运行: 我肯定在这里想念的东西:) 问题答案: 您还可以使用Stream.reduce(identity,accumulator,combiner)。 身份 是减少函数的初始值。 累加器 减少到,如果流是 顺序的 ,这是下一个减少的条件。 合路器 永远不要

  • 问题内容: 我喜欢新的Java8 StreamAPI,并希望不仅将其用于一个文件。通常,我使用以下代码: 但是,如果可能的话,如何在一个流中读取两个文件呢? 问题答案: 没有任何额外的帮助程序功能或外部库,最简单的方法是: 如果尚未声明抛出受检查的异常,则可以 但是,a,我们不能这样做。有几种解决方法。一种是制作自己的版本,将其称为标准版本,然后将其作为捕获并重新抛出。另一种方法是使用抛出检查异常

  • 问题内容: 我有一个带有以下签名的类: 我想要一个来自a 的键,该键将是该类的名称。我如何使用java8流按es名称对列表进行分组?所有es在名称上都是唯一的。 可以在单个流中求解,还是应该以某种方式拆分它还是采用经典解决方案? 问题答案:

  • 问题内容: 使用Homebrew进行安装似乎不再起作用。运行后: 我收到以下错误: 简单地做: 错误出在: 这似乎是最新的发展,因为我记得几个月前以这种方式安装它。有关如何在当今的MacOS 上正确安装的任何建议? 问题答案: 这已经作为github问题得到了回答:https : //github.com/Homebrew/homebrew-cask- versions/issues/7253 T

  • 问题内容: 我需要将Java转换为的实例(包括地图内容) 我应该怎么做才能使此代码可编译? 问题答案: 从Collectors.toMap(…)javadoc: 例如: