Java Stream collectors: 如何使用计数创建总计

huangapple go评论61阅读模式
英文:

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>,其中总计了具有相同的codedescription(这是关键)的Result中每个value的总和,并统计这个键在结果列表中出现的次数。为了创建总和,我考虑使用Durationplus(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&lt;Result&gt; I have to generate a Map&lt;Product, Total&gt; 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 &lt;Product, Total&gt; map = results.stream()
                                  .collect(Collectors.groupingBy(p -&gt; new Product(p.getCode(), p.getDescription()),
    Collectors.collectingAndThen(Collectors.toList(), list -&gt; {
        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&lt;Product, Total&gt; map = results.stream()
  .collect(Collectors.groupingBy(r -&gt; 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 Lists, just to perform another Reduction afterwards. Perform the Reduction in the first place:

Map&lt;Product, Total&gt; map = results.stream()
  .collect(Collectors.groupingBy(r -&gt; 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&lt;Product, Total&gt; calc(List&lt;Result&gt; results) {
    return results.stream().collect(Collectors.groupingBy(
            result -&gt; new Product(result.getCode(), result.getDescription()),
            Collectors.mapping(Result::getValue, new Collector&lt;Duration, Total, Total&gt;() {
                @Override
                public Supplier&lt;Total&gt; supplier() {
                    return () -&gt; new Total(Duration.ZERO, 0);
                }

                @Override
                public BiConsumer&lt;Total, Duration&gt; accumulator() {
                    return (total, duration) -&gt; {
                        total.setTotal(total.getTotal().plus(duration));
                        total.setCount(total.getCount() + 1);
                    };
                }

                @Override
                public BinaryOperator&lt;Total&gt; combiner() {
                    return (one, two) -&gt; new Total(one.getTotal().plus(two.getTotal()),
                                                   one.getCount() + two.getCount());
                }

                @Override
                public Function&lt;Total, Total&gt; finisher() {
                    return Function.identity();
                }

                @Override
                public Set&lt;Characteristics&gt; 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&lt;Product, Total&gt; ans = results.stream().collect(groupingBy(r -&gt; new Product(r.code, r.description), collectingAndThen(toList(), list -&gt; {
long count = list.size();
Duration total = list.stream().map(r -&gt; r.value).reduce((o1, o2) -&gt; 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&lt;Product, Total&gt; result = resultList.stream()
.collect(Collectors.groupingBy(
r -&gt; new Product(r.getCode(), r.getDescription()),
Collectors.reducing(
new Total(Duration.ZERO, 0),
r -&gt; new Total(r.getDuration(), 1),
(t1, t2) -&gt; new Total(t1.getDuration().plus(t2.getDuration()), t1.getCount() + t2.getCount())
)
));

huangapple
  • 本文由 发表于 2023年5月7日 06:04:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76191366.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定