【问题标题】:How does combiner in Stream.collect method work in java 8?Stream.collect 方法中的组合器如何在 java 8 中工作?
【发布时间】:2015-07-09 16:29:22
【问题描述】:

我创建了简单的演示:

public static void main(String[] args) {
        List<String> list2 = Arrays.asList("adf", "bcd", "abc", "hgr", "jyt", "edr", "biu");

String collect = list2.stream().collect(String::new, (res, elem) -> {
              res=res.concat(" ").concat(elem);
//            System.out.printf("res=%s, elem=%s\n", res.isEmpty(), elem);

        }, (res1, res2) -> {
            System.out.printf("res1=%s, res2=%s\n", res1, res2);            
        });
        System.out.println("collect=" + collect);
}

问题是BiConsumer combiner 的一部分collect 根本没有运行。

如果我使用parallelStream(),它会运行,但两个参数res1res2 等于供应商String::new

如何使combinercollect 方法中工作?

【问题讨论】:

  • 请注意,如果您想通过分隔符连接元素,请使用.collect(Collectors.joining(" "));String.join(" ", list2)(如果您有字符串列表)。
  • 请注意,您的尝试更像是 reduce,因为您使用的是不可变的Strings:list2.stream().reduce("", (res, elem) -&gt; res.concat(" ").concat(elem))。不过,作为@Alexis C. pointed out,已经有更高效的内置解决方案了。

标签: java java-8 java-stream


【解决方案1】:

首先,没有必要在非并行流中执行组合器,因为没有要组合的东西。

其次,您的问题源于使用String::newString.concat。累加器应该通过将第二个参数与其组合来修改第一个参数,但由于 Java 中的字符串是不可变的,因此您的代码不会产生任何结果。

          res=res.concat(" ").concat(elem);

将创建一个新字符串,然后将其丢弃。您想改用 StringBuilder 以便保留中间结果:

public static void main(String[] args) {
    List<String> list2 = Arrays.asList("adf", "bcd", "abc", "hgr", "jyt", "edr", "biu");

    String collect = list2.stream().collect(StringBuilder::new, (res, elem) -> {
        res.append(" ").append(elem);
    }, (res1, res2) -> {
        res1.append(res2.toString());
        System.out.printf("res1=%s, res2=%s\n", res1, res2);
    }).toString();
    System.out.println("collect=" + collect);
}

这也适用于并行流

res1= hgr jyt, res2= jyt
res1= bcd abc, res2= abc
res1= adf bcd abc, res2= bcd abc
res1= edr biu, res2= biu
res1= hgr jyt edr biu, res2= edr biu
res1= adf bcd abc hgr jyt edr biu, res2= hgr jyt edr biu
收集= adf bcd abc hgr jyt edr biu

【讨论】:

  • 当我运行这个例子时,我看到组合器只能在并行流中工作(不是 ALSO) - 否则在单个流中你只能执行最后一个 System.out.println("collect =" + 收集);
  • @Raniz,为什么使用 String::new 会导致创建一个新字符串然后丢弃它?
  • 不是String::new 而是String::concat 这就是问题所在。 API 期望收集器就地修改结果,而不是像String::concat那样创建一个新对象,因为字符串是不可变的。
【解决方案2】:

我认为combiner 仅用于并行流(用于合并并行计算的部分输出),因此请让您的流并行。

String collect = list2.parallelStream().collect(...

【讨论】:

  • @AlexisC。我想另一部分是我回答后添加的。
  • 没有什么能阻止你更新你的答案......我认为有趣的部分是解释 OP 做错了什么以及 “如何让组合器在 collect 方法中工作?”(尽管累加器也错了)
【解决方案3】:

对于Raniz 样本,无论是否平行,结果都会更有趣:

    String collect = list2.stream().collect(StringBuilder::new,
            (res, elem) -> {
                System.out.printf("ACCUMULATE res=%s, elem=%s\n", res, elem);
                res.append(" ").append(elem);
        },
            (res1, res2) -> {
                System.out.printf("COMBINE res1=%s, res2=%s\n", res1, res2);
                res1.append(res2.toString());
            }).toString();

如果没有并行组合永远不会被调用:

ACCUMULATE res=, elem=adf
ACCUMULATE res= adf, elem=bcd
ACCUMULATE res= adf bcd, elem=abc
ACCUMULATE res= adf bcd abc, elem=hgr
ACCUMULATE res= adf bcd abc hgr, elem=jyt
ACCUMULATE res= adf bcd abc hgr jyt, elem=edr
ACCUMULATE res= adf bcd abc hgr jyt edr, elem=biu
collect= adf bcd abc hgr jyt edr biu

平行

ACCUMULATE res=, elem=jyt
ACCUMULATE res=, elem=hgr
COMBINE res1= hgr, res2= jyt
ACCUMULATE res=, elem=biu
ACCUMULATE res=, elem=edr
COMBINE res1= edr, res2= biu
ACCUMULATE res=, elem=bcd
COMBINE res1= hgr jyt, res2= edr biu
ACCUMULATE res=, elem=abc
ACCUMULATE res=, elem=adf
COMBINE res1= bcd, res2= abc
COMBINE res1= adf, res2= bcd abc
COMBINE res1= adf bcd abc, res2= hgr jyt edr biu
collect= adf bcd abc hgr jyt edr biu

【讨论】:

  • 为什么要在 res2 上调用 toString 方法?有必要吗?我试图删除它,它工作正常。
  • 很好,谢谢!你是什​​么意思“并行”?您的意思是您在第二个示例中使用了 parallelStream,因此它是多线程运行的?
  • @aderchox 表示stream().parallel()
猜你喜欢
  • 1970-01-01
  • 2019-09-22
  • 2015-09-30
  • 1970-01-01
  • 2022-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多