java streams: straightforward reduce

跟風遠走 提交于 2019-12-10 13:16:35

问题


I've a stream of MetricGroup, where:

public class MetricGroup {

    private int uploadedDocs;
    private long uploadedKbs;

    // getters and setters

}

I need to sumarize all metrics in one single metric. I mean, I need to add all metric.uploadedDocs into a sumMetric.uploadedDocs and metric.uploadedKds into a sumMetric.uploadedKbs.

I figure out I need some kind of reduce

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

Any ideas?


回答1:


You can use this overload of reduce:

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

like this:

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

You can also use the collect method:

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
)



回答2:


To avoid the creation of several/many MetricGroup objects during the reduce call, you can make two separate calls to sum the UploadedDocs and UploadedKbs respectively and then construct a new MetricGroup representing the result.

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

Arguably more readable as well...




回答3:


Simply pass in a single lambda (will manipulate existing 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)]

Or, to really get a new MetricGroup (without manipulating an existing one)

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



回答4:


As always with java streams, you don't really have to use them. I suggest creating a simple reduction helper-method:

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);
}

If you can't change that you start with a stream you can still use above method, by just passing a reference to the Stream.iterator() method:

MetricGroup reduced = reduce(stream::iterator);



回答5:


If you want to use reduction, I'd recommend making MetricGroup be a value type, by making the fields final, adding a zero, and replacing the setters with combining methods.

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);
    }
}

This will let you perform stream operations nicely:

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


来源:https://stackoverflow.com/questions/54089939/java-streams-straightforward-reduce

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!