使用Java 8减少链接的最佳方式

Hen*_*kes 42 java java-8

我有以下代码,我正在努力改进:

BigDecimal total = entity.getAssociate().stream().map(Associates::getPropertyA)
    .reduce(BigDecimal.ZERO, BigDecimal::add);
total = entity.getAssociate().stream().map(Associates::getPropertyB)
    .reduce(total, BigDecimal::add);
total = entity.getAssociate().stream().map(Associates::getPropertyC)
    .reduce(total, BigDecimal::add);
total = entity.getAssociate().stream().map(Associates::getPropertyD)
    .reduce(total, BigDecimal::add);
Run Code Online (Sandbox Code Playgroud)

它有效,但它确实有一个更好的方法来做到这一点.有人可以就此事启发我吗?

Era*_*ran 59

如果所有这些属性都属于同一类型(似乎它们都是相同的BigDecimal),您可以使用flatMap它们创建所有属性Stream,然后使用reduce总和:

BigDecimal total = 
    entity.getAssociate()
          .stream()
          .flatMap (a -> Stream.of(a.getPropertyA(),a.getPropertyB(),a.getPropertyC(),a.getPropertyD()))
          .reduce(BigDecimal.ZERO, BigDecimal::add);
Run Code Online (Sandbox Code Playgroud)


Man*_*dis 32

您可以简单地在地图中链式添加所有属性:

BigDecimal total = entity.getAssociate().stream()
            .map(a -> a.getPropertyA()
                    .add(a.getPropertyB())
                    .add(a.getPropertyC())
                    .add(a.getPropertyD()))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
Run Code Online (Sandbox Code Playgroud)

请注意,这会更改添加数字的顺序.


Fed*_*ner 23

如果您可以将以下方法添加到Associates类中:

public BigDecimal getSubtotal() {
    return propertyA.add(propertyB).add(propertyC).add(propertyD);
}
Run Code Online (Sandbox Code Playgroud)

然后,完成任务很容易:

BigDecimal total = entity.getAssociate().stream()
    .map(Associate::getSubtotal)
    .reduce(BigDecimal::add)
    .orElse(BigDecimal.ZERO);
Run Code Online (Sandbox Code Playgroud)


Mar*_*o13 16

"更好"或"最佳"这两个词应该指代某些指标.性能?可读性?优雅?

Eran答案显示了一种方法,即为每个伙伴创建包含属性值A,B,C和D的小流,并将这些值平面映射到更大的流中.这种方法的求和顺序是

A0 + B0 + C0 + D0  +  A1 + B1 + C1 + D1  + ... +  An + Bn + Cn + Dn
Run Code Online (Sandbox Code Playgroud)

另一种选择是创建属性A,B,C和D的各个流,并在应用缩减之前连接这些流.这可以通过嵌套Stream#concat调用完成,但使用flatMap标识函数更优雅灵活:

Stream<BigDecimal> stream = Stream.of(
    entity.getAssociate().stream().map(Associates::getPropertyA),
    entity.getAssociate().stream().map(Associates::getPropertyB),
    entity.getAssociate().stream().map(Associates::getPropertyA),
    entity.getAssociate().stream().map(Associates::getPropertyC))
    .flatMap(Function.identity());

BigDecimal total = stream.reduce(BigDecimal.ZERO, BigDecimal::add);
Run Code Online (Sandbox Code Playgroud)

关键是在这种情况下,求和顺序是

A0 + A1 + ... + An + B0 + B1 + ... + Bn + C0 + C1 + ... + Cn 
Run Code Online (Sandbox Code Playgroud)

(从技术上讲,这可能不会产生很大的差异.但是这种方法在概念上与迄今为止提出的方法(就总和顺序而言)不同,因此,作为一种选择可能值得一提 - 另外,它更类似于您当前使用的方法,但没有用于减少的破坏的标识值)


Eug*_*ene 6

或者只是一个forEach:

BigDecimal[] total = new BigDecimal[] { BigDecimal.ZERO };
entity.getAssociate().stream().forEach(a -> {
    total[0] = total[0].add(a.getPropertyA());
    // ... and so on for all others
});
Run Code Online (Sandbox Code Playgroud)

作为一个方面 - 不是你当前的实现是错误的,因为你违反identity了减少.

  • 我贬低了.这个问题要求以某种方式做某事.对我来说,这意味着"我知道我拥有的东西(减少),我不知道如何使用它".因此,虽然我同意这个答案有效地解决了这个问题,但它没有按照预期回答问题.其次,这个答案引入了处理引用的负担(这种情况下的数组,但可能是任何东西,甚至是'AtomicReference`).说到这一点,如果需要,"AtomicReference"可以更好地处理正确的并行性.总而言之,这是一个非常糟糕的流使用. (4认同)
  • 以防我在这里值得投票,评论也将受到赞赏 (3认同)