Java Streams:直接减少

时间:2019-01-08 10:34:20

标签: java java-8 java-stream

我有MetricGroup的信息流,其中:

public class MetricGroup {

    private int uploadedDocs;
    private long uploadedKbs;

    // getters and setters

}

我需要将所有指标汇总为一个指标。我的意思是,我需要将所有metric.uploadedDocs添加到sumMetric.uploadedDocs中,并将metric.uploadedKds添加到sumMetric.uploadedKbs中。

我发现我需要某种reduce

Stream.of(new MetricGroup(1,100), new MetricGroup(1,200))
    .reduce(????);

有什么想法吗?

5 个答案:

答案 0 :(得分:7)

您可以使用reduce的此重载:

T reduce(T identity,
     BinaryOperator<T> accumulator)

像这样:

.reduce(new MetricGroup(0, 0),
        (x, y) -> new MetricGroup(
                      x.getUploadedDocs() + y.getUploadedDocs()
                      x.getUploadedKbs() + y.getUploadedKbs()
                  )
        )

您还可以使用collect方法:

private static MetricGroup combine(MetricGroup x, MetricGroup y) {
    x.setUploadedDocs(x.getUploadedDocs() + y.getUploadedDocs());
    x.setUploadedKbs(x.getUploadedKbs() + y.getUploadedKbs());
    return x;
}

// ....

.collect(() -> new MetricGroup(0, 0),
    YourClass::combine,
    YourClass::combine
)

答案 1 :(得分:3)

为避免在MetricGroup调用过程中创建多个reduce对象,可以进行两个单独的调用来分别对UploadedDocsUploadedKbs求和,然后构造代表结果的新MetricGroup

int uploadedDocsSum = source.stream().mapToInt(MetricGroup::getUploadedDocs).sum();
long uploadedKbsSum = source.stream().mapToLong(MetricGroup::getUploadedKbs).sum();
MetricGroup result = new MetricGroup(uploadedDocsSum, uploadedKbsSum);

也可以说更具可读性...

答案 2 :(得分:2)

只需传入一个lambda(将操作现有的MetricGroup)

Stream.of(new MetricGroup(1, 100), new MetricGroup(1, 200))
    .reduce((a, b) -> {
      a.setUploadedDocs(a.getUploadedDocs() + b.getUploadedDocs());
      a.setUploadedKbs(a.getUploadedKbs() + b.getUploadedKbs());
      return a;
    });

// Optional[F.MetricGroup(uploadedDocs=2, uploadedKbs=300)]

或者,要真正获得一个新的MetricGroup(无需操纵现有的MetricGroup)

Stream.of(new MetricGroup(1, 100), new MetricGroup(1, 200))
        .reduce((a, b) -> new MetricGroup(a.getUploadedDocs() + b.getUploadedDocs(), a.getUploadedKbs() + b.getUploadedKbs()));

答案 3 :(得分:2)

与java流一样,您实际上不必使用它们。我建议创建一个简单的减少辅助方法:

public static MetricGroup reduce(Iterable<? extends MetricGroup> metrics){
   int uploadedDocs = 0;
   long uploadedKbs = 0L;
   for(MetricGroup metric : metrics){
       uploadedDocs += metric.getUploadedDocs();
       uploadedKbs += metric.getUploadedKbs();
   }
   return new MetricGroup(uploadedDocs, uploadedKbs);
}

如果您不能更改以流开头,则仍然可以使用上述方法,只需传递对Stream.iterator()方法的引用即可:

MetricGroup reduced = reduce(stream::iterator);

答案 4 :(得分:1)

如果要使用归约法,建议将MetricGroup设为值类型,方法是将字段定为最终字段,添加零,然后用组合方法替换设置器。

public class MetricGroup {
    private final int uploadedDocs;
    private final long uploadedKbs;

    // obvious constructor
    // getters

    public static final ZERO = new MetricGroup(0, 0);

    public MetricGroup add(MetricGroup a, MetricGroup b) {
        return new MetricGroup(a.uploadedDocs + b.upLoadedDocs,
                               a.uploadedKbs + b.uploadedKbs);
    }

    public MetricGroup uploadOneDoc(long kbs) {
        return new MetricGroup(uploadedDocs + 1, uploadedKbs + kbs);
    }
}

这将使您很好地执行流操作:

MetricGroup sum = metricGroups.stream()
                              .reduce(MetricGroup.ZERO, MetricGroup::add);