我有一个关于字数的代码段:
String[] wordCountArr = {"a", "b", "c", "a", "a", "b", "c", "d", "e"};
Stream.of(wordCountArr).collect(TreeMap::new,
(treeMap, str) -> {
Object countValue = treeMap.get(str);
if (countValue != null) {
Integer count = Integer.valueOf(countValue.toString());
treeMap.put(str, count + 1);
}
else {
treeMap.put(str, 1);
}
}, (treeMap, treeMap2) -> {
treeMap.putAll(treeMap2);
}).entrySet()
.forEach(System.out::println);
它获得预期值:a = 3 b = 2 c = 2 d = 1 e = 1,但似乎没有执行collect函数的Combiner。然后我得到了这个:Java 8 Stream - Reduce function's combiner not getting executed并将代码更改为:
Stream.of(wordCountArr).parallel().collect(TreeMap::new,
(treeMap, str) -> {
Object countValue = treeMap.get(str);
if (countValue != null) {
Integer count = Integer.valueOf(countValue.toString());
treeMap.put(str, count + 1);
}
else {
treeMap.put(str, 1);
}
}, (treeMap, treeMap2) -> {
treeMap.putAll(treeMap2);
}).entrySet()
.forEach(System.out::println);
但结果不是预期的:a = 1 b = 1 c = 1 d = 1 e = 1,我想putAll函数可能只是替换旧的映射。有什么好主意可以得到正确的结果吗?使用并行流是否更有效?谢谢!
解决:
What is the best practices to merge two maps将putAll替换为合并
答案 0 :(得分:2)
如果框架在使用并行流时尝试连接多个fork的结果,则仅执行组合器。
所以在第一个版本中,合并器没有执行。
您的第二个代码版本可能会导致ConcurrentModificationException,因为在使用并行流时,TreeMap不是线程安全的。
还有一点是当你组合两棵树时,你忘记了对两棵树中的值求和。您将treeMap
的所有内容与treeMap2
合并,因此treeMap
中的当前值将被丢弃:treeMap.putAll(treeMap2);
您必须手动迭代treeMap
中的密钥,将值与treeMap2
相加并进行回放。
我不知道你为什么提出这种方法,但要计算每组的项目,你只需使用groupingBy
:
Map<String, Long> countMap = Stream.of(wordCountArr).collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
答案 1 :(得分:0)
String[] wordCountArr = {"a", "b", "c", "a", "a", "b", "c", "d", "e"};
Map<String, Long> countMap = Stream.of(wordCountArr).collect(Collectors.groupingBy(letter -> letter, Collectors.counting()));
countMap.forEach((s, count) -> System.out.println(s + " : " + count));