我想计算一个流的不同元素,我想知道为什么
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));
不起作用。 Eclipse告诉我
收集器类型中的toMap(Function,Function,BinaryOperator)方法不适用于参数((s) - &gt; {},int,Integer :: sum)
顺便说一句,我知道这个解决方案:
Map<String, Long> counter2 = stream.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
所以我有两个问题:
编辑:我自己解决了第一个问题:
Map<String, Integer> counter1 = stream.collect(Collectors.toMap(s -> s, s -> 1, Integer::sum));
Java期待一个函数作为第二个参数。
答案 0 :(得分:5)
确实有几种方法可以做到。你没有提到的是.collect(groupingBy(x -> x, summingInt(x -> 1)));
性能存在一些差异。
如果每个桶的对象非常少,方法#1将处于最佳状态。在每桶只有一个对象的理想情况下,您最终会立即得到最终地图,而无需修改条目。在最糟糕的情况下,有大量重复的物体,它将不得不进行大量的装箱/拆箱。
方法#2依赖于counting()
收集器,它没有详细说明它应该如何进行计数。当前的实现转发到reducing
但可能会发生变化。
summingInt
方法将累计int
而不是Integer
,因此不需要任何装箱/拆箱。如果对象重复很多次,它将处于最佳状态。
至于选择哪一个,最好编码以便在必要时进行清晰度和优化。对我而言,groupingBy(x->x, counting())
最清楚地表达了意图,所以这是我赞成的。
答案 1 :(得分:0)
如果您不使用并行流,则以下内容应该起作用:
final int[] counter = {0};
stream.peek(s -> counter[0]++);
答案 2 :(得分:0)
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/