15

我想计算流的不同元素,想知道为什么

Stream<String> stream = Stream.of("a", "b", "a", "c", "c", "a", "a", "d");
Map<String, Integer> counter1 = stream.collect(Collectors.toMap(s -> s, 1, Integer::sum));

不起作用。日食告诉我

类型 Collectors 中的 toMap(Function, Function, BinaryOperator) 方法不适用于参数 (( s) -> {}, int, Integer::sum)

顺便说一句,我知道那个解决方案:

Map<String, Long> counter2 = stream.collect(Collectors.groupingBy(s -> s, Collectors.counting()));

所以我有两个问题:

  1. 我的第一种方法有什么错误?
  2. 你将如何实现这样的计数器?

编辑:我自己解决了第一个问题:

Map<String, Integer> counter1 = stream.collect(Collectors.toMap(s -> s, s -> 1, Integer::sum)); 

Java 期望一个函数作为第二个参数。

4

3 回答 3

11

There are indeed several ways to do it. The one you haven't mentioned is .collect(groupingBy(x -> x, summingInt(x -> 1)));

There are some differences in performance.

Approach #1 is going to be at its best if there are very few objects per bucket. In the ideal case of only 1 object per bucket, you end up with the final map right away with no need to modify the entries. In the worst case of having a very large number of repeated objects, it will have to do a lot of boxing/unboxing.

Approach #2 relies on counting() collector, which doesn't specify exactly how it should do the counting. The current implementation forwards to reducing but that might change.

The summingInt approach will accumulate the count in int rather than Integer and thus will not require any boxing/unboxing. It will be at its best if objects repeat a very large number of times.

As for which one to choose, it is best to code for clarity and optimize when it becomes necessary. To me, groupingBy(x->x, counting()) expresses the intent most clearly, so that's the one I would favor.

于 2015-05-26T07:28:57.577 回答
1
    private Collector<CustomObject.class, int[], Integer> customIntegerCountingCollector() {
    return Collector.of(
        () -> new int[1],
        (result, ) -> result[0] += 1,
        (result1, result2) -> {
            result1[0] += result2[0];
            return result1;
        },
        total -> Integer.valueOf(total[0])
    );
}

灵感来自:https ://www.deadcoderising.com/2017-03-07-java-8-creating-a-custom-collector-for-your-stream/

于 2020-07-23T20:57:20.613 回答
-3

如果您不使用并行流,则以下内容应该有效:

final int[] counter = {0};
stream.peek(s -> counter[0]++);
于 2019-11-26T16:56:20.857 回答