【问题标题】:java8 stream sum multiplejava8流总和倍数
【发布时间】:2016-11-12 21:39:21
【问题描述】:

我很好奇,如何在一个 java8 流中总结多个变量。

Integer wCPU = 0;
Double wnetwork = 0.0;
Double wMem = 0.0;

this.slaContractList.forEach(sla -> {
    wCPU += sla.getNumberOfCPUs();
    wnetwork += sla.getNetworkBandwith();
    wMem += sla.getMemory();
});

但是,这不会编译,因为 lambda 表达式中的变量应该是 final。

【问题讨论】:

  • 你想要一个自定义收集器,看stackoverflow.com/questions/37187541/…
  • hack 的缺点是什么?它似乎有更少的代码行,这很好。
  • 你说的......这是一个黑客。并行运行它,它可能不会产生正确的输出,因为+=,如++is not an atomic operation
  • 您如何看待@Lukasz 添加的最新答案,该答案看起来也很简洁,并且没有 Hack 的缺点。

标签: java collections java-8 sum java-stream


【解决方案1】:

假设slaContractListSlaContract 对象的列表,并且它有构造函数SlaContract(numberOfCPUs, networkBandwith, memory) 你可以:

SlaContract sumContract = slaContractList.stream()
    .reduce(new SlaContract(0, 0.0, 0.0), (sla1, sla2) -> {
         return new SlaContract(sla1.getNumberOfCPUs() + sla2.getNumberOfCPUs(), sla1.getworkBandwith() + sla2.getworkBandwith(), sla1.getMemory() + sla2.getMemory());
    });

Double wnetwork = sumContract.getworkBandwith();
Double wMem = sumContract.getMemory();
Integer wCPU = sumContract.getNumberOfCPUs();

相同的解决方案,但对于简单的类:

Point sumPoint = pointsList.stream()
    .reduce(new Point(0, 0), (p1, p2) -> {
         return new Point(p1.x + p2.x, p1.y + p2.y);
    });

【讨论】:

  • reduce 不应在累积期间修改其输入参数。这将在并行运行时中断,它应该始终返回一个包含累积的新对象。 See also。你想在这里使用collect
【解决方案2】:

尝试使用Stream.reduceStream.sum

Double wnetwork = slaContractList.stream()
            .mapToDouble(sla -> sla.getNetworkBandwith())
            .sum();

Double wMem = slaContractList.stream()
            .mapToDouble(sla -> sla.getMemory())
            .sum();

Integer wCPU = slaContractList.stream()
            .mapToInt(sla -> sla.getNumberOfCPUs())
            .sum();

https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html

使用流的优点是可以选择使用parallelStream() 而不是stream()。在某些情况下,它可能比简单的循环更有效。

【讨论】:

  • 所以你会为每个属性使用一个映射?这不是单循环慢吗?
  • 问题是它基本上是一个元组流。而且我相信如果将元组中的每个值单独汇总,但在循环的单次通过中会更快。
  • 嗯,这就是对流求和的方式。将此解决方案与其他不使用流的解决方案进行比较。检查parallelStream() 而不是stream()
  • long t0 = System.nanoTime(); //这里解决long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("solution took: %d ms", millis));
  • 只是出于好奇,请问在您的情况下哪种解决方案更快? (只需复制之前评论中的一些代码)
【解决方案3】:

只是为了求和,我会像在 Łukasz 答案中那样使用流的求和运算,但是,对于解决“最终问题”的更通用的解决方案,您可以使用 java.util.concurrent.atomic 的类.它旨在用于流中并且是线程安全的,因此可以在并行流中使用。

AtomicInteger wCPU = new AtomicInteger();
DoubleAccumulator wnetwork = new DoubleAccumulator(Double::sum,0.d);
DoubleAccumulator wMem = new DoubleAccumulator(Double::sum,0.d);

this.slaContractList.forEach(sla -> {
    wCPU.addAndGet(sla.getNumberOfCPUs());
    wnetwork.accumulate(sla.getNetworkBandwith());
    wMem.accumulate(sla.getMemory());
});

现在你看到有两种实现:Accumulator 和 Atomic,这两种实现之间的选择是另一个问题:

java 8 : Are LongAdder and LongAccumulator preferred to AtomicLong?

【讨论】:

  • 这似乎比创建额外的构造函数更干净。
【解决方案4】:

我会像这样做一个简单的黑客攻击:

    Integer[] wCPU = new Integer[1];
    Double[] wnetwork = new Double[1];
    Double[] wMem = new Double[1];

    this.slaContractList.forEach(sla -> {
        wCPU[0] += sla.getNumberOfCPUs();
        wnetwork[0] += sla.getNetworkBandwith();
        wMem[0] += sla.getMemory();
    });

两者都可以使用final 关键字,因为在Java 8 中他们引入了有效的最终 概念。这意味着,您只分配了一次。

【讨论】:

  • 我喜欢你的 hack ;)
  • 抱歉切换。但我认为另一个答案更好/不是黑客。
  • 这些是“概念上”的可变整数和双精度数,唯一的问题是它不是线程安全的,但它适用于使用otu 线程的简单问题。
猜你喜欢
  • 2015-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 2019-01-08
相关资源
最近更新 更多