英文:
Java Stream collectors: how to create total with counting
问题
I have a list of objects of class Result
defined as:
class Result {
String code;
String description;
Duration value;
}
and these two classes:
class Product {
String code;
String description;
}
class Total {
Duration total;
long count;
}
给定List<Result>
,我需要生成一个Map<Product, Total>
,其中总计了具有相同的code
和description
(这是关键)的Result
中每个value
的总和,并统计这个键在结果列表中出现的次数。为了创建总和,我考虑使用Duration
的plus(Duration)
方法。如何实现这个功能?谢谢帮助。
更新:
根据评论的提示,我能够做到这一点:
(请问有人能告诉我这是否正确?)
Map<Product, Total> map = results.stream()
.collect(Collectors.groupingBy(p -> new Product(p.getCode(), p.getDescription()),
Collectors.collectingAndThen(Collectors.toList(), list -> {
Duration total = list.stream().map(Result::getValue).reduce(Duration.ZERO, Duration::plus);
long count = list.size();
return new Total(total, count);
})));
英文:
I have a list of objects of class Result
defined as:
class Result {
String code;
String description;
Duration value;
}
and these two classes:
class Product {
String code;
String description;
}
class Total {
Duration total;
long count;
}
Given the List<Result>
I have to generate a Map<Product, Total>
where in total I have the sum of every value in Result
that has the same code
and description
(this is key) and as count how many result with this key is present in the list of results.
For creating the total sum I consider to use plus(Duration)
of Duration
.
How can I do this?
Thanks for the help.
UPDATE:
From the comments hints I was able to do this:
(Can someone tell me if it can be correct?)
Map <Product, Total> map = results.stream()
.collect(Collectors.groupingBy(p -> new Product(p.getCode(), p.getDescription()),
Collectors.collectingAndThen(Collectors.toList(), list -> {
Duration total = list.stream().map(Result::getValue).reduce(Duration.ZERO, Duration::plus);
long count = list.size();
return new Total(total, count);
})));
</details>
# 答案1
**得分**: 3
你的方法正在工作并且形式上是正确的,但将所有元素收集到 `List` 中,然后再执行另一个 Reduction 操作是不划算的。请在首次执行 Reduction 操作:
```java
Map<Product, Total> map = results.stream()
.collect(Collectors.groupingBy(r -> new Product(r.getCode(), r.getDescription()),
Collectors.teeing(
Collectors.reducing(Duration.ZERO, Result::getValue, Duration::plus),
Collectors.counting(),
Total::new)));
英文:
Your approach is working and formally correct, but it is wasteful to collect all elements into List
s, just to perform another Reduction afterwards. Perform the Reduction in the first place:
Map<Product, Total> map = results.stream()
.collect(Collectors.groupingBy(r -> new Product(r.getCode(), r.getDescription()),
Collectors.teeing(
Collectors.reducing(Duration.ZERO, Result::getValue, Duration::plus),
Collectors.counting(),
Total::new)));
答案2
得分: 2
你可以编写自己的 Collector
:
public static Map<Product, Total> calc(List<Result> results) {
return results.stream().collect(Collectors.groupingBy(
result -> new Product(result.getCode(), result.getDescription()),
Collectors.mapping(Result::getValue, new Collector<Duration, Total, Total>() {
@Override
public Supplier<Total> supplier() {
return () -> new Total(Duration.ZERO, 0);
}
@Override
public BiConsumer<Total, Duration> accumulator() {
return (total, duration) -> {
total.setTotal(total.getTotal().plus(duration));
total.setCount(total.getCount() + 1);
};
}
@Override
public BinaryOperator<Total> combiner() {
return (one, two) -> new Total(one.getTotal().plus(two.getTotal()),
one.getCount() + two.getCount());
}
@Override
public Function<Total, Total> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Set.of(Characteristics.UNORDERED);
}
})));
}
如果您有其他翻译需求,请告诉我。
英文:
You can write your own Collector
:
public static Map<Product, Total> calc(List<Result> results) {
return results.stream().collect(Collectors.groupingBy(
result -> new Product(result.getCode(), result.getDescription()),
Collectors.mapping(Result::getValue, new Collector<Duration, Total, Total>() {
@Override
public Supplier<Total> supplier() {
return () -> new Total(Duration.ZERO, 0);
}
@Override
public BiConsumer<Total, Duration> accumulator() {
return (total, duration) -> {
total.setTotal(total.getTotal().plus(duration));
total.setCount(total.getCount() + 1);
};
}
@Override
public BinaryOperator<Total> combiner() {
return (one, two) -> new Total(one.getTotal().plus(two.getTotal()),
one.getCount() + two.getCount());
}
@Override
public Function<Total, Total> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Set.of(Characteristics.UNORDERED);
}
})));
}
答案3
得分: 0
你可以尝试这种方式。
Map<Product, Total> ans = results.stream().collect(groupingBy(r -> new Product(r.code, r.description), collectingAndThen(toList(), list -> {
long count = list.size();
Duration total = list.stream().map(r -> r.value).reduce((o1, o2) -> o1.plus(o2)).get();
return new Total(total, count);
})));
英文:
You could try this way.
Map<Product, Total> ans = results.stream().collect(groupingBy(r -> new Product(r.code, r.description), collectingAndThen(toList(), list -> {
long count = list.size();
Duration total = list.stream().map(r -> r.value).reduce((o1, o2) -> o1.plus(o2)).get();
return new Total(total, count);
})));
答案4
得分: 0
你可以这样做:
Map<Product, Total> result = resultList.stream()
.collect(Collectors.groupingBy(
r -> new Product(r.getCode(), r.getDescription()),
Collectors.reducing(
new Total(Duration.ZERO, 0),
r -> new Total(r.getDuration(), 1),
(t1, t2) -> new Total(t1.getDuration().plus(t2.getDuration()), t1.getCount() + t2.getCount())
)
));
英文:
you can do it like this
Map<Product, Total> result = resultList.stream()
.collect(Collectors.groupingBy(
r -> new Product(r.getCode(), r.getDescription()),
Collectors.reducing(
new Total(Duration.ZERO, 0),
r -> new Total(r.getDuration(), 1),
(t1, t2) -> new Total(t1.getDuration().plus(t2.getDuration()), t1.getCount() + t2.getCount())
)
));
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论