在Java中使用并行流但未获得预期值

时间:2018-05-09 04:22:54

标签: java

我有一个关于字数的代码段:

    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替换为合并

2 个答案:

答案 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));