Stream.map/filter 中的 Lambda 未调用

use*_*ser 7 java lambda functional-programming java-8 java-stream

我试图List通过将它们添加到 aSetList同时使用Stream.filterStream.map

List<String> strings = Arrays.asList("foo", "bar", "foo", "baz", "foo", "bar");

Set<String> distinct = new HashSet<>();
List<String> extras = new ArrayList<>();

strings
  .stream()
  .filter(x -> !distinct.add(x))
  .map(extra -> extras.add(extra));
Run Code Online (Sandbox Code Playgroud)

最后,我希望distinct[foo, bar, baz]extras将是[foo, foo, bar],因为有 2 个额外的 实例foo和 1 个bar。但是,在我运行这个之后它们都是空的。

从未调用过提供给流的 lambda,我通过尝试在里面打印来验证map

.map(extra -> {
  System.out.println(extra);
  return extras.add(extra);
})
Run Code Online (Sandbox Code Playgroud)

当我尝试putMap两者一起使用时,这不起作用。我究竟做错了什么?


注意:可能还有其他与此类似的问题,但我正在寻找一种规范的答案,以解释为什么此类内容不适用于 Java 8 的 Streams。如果你能把它变成一个更普遍的问题(即使这意味着完全改变它),我会很感激。

hev*_*ev1 10

两个Stream#filterStream#map是中间操作,这意味着它们被懒惰地评估。根据文档:

中间操作返回一个新的流。他们总是很懒惰;执行诸如 filter() 之类的中间操作实际上并不执行任何过滤,而是创建一个新流,该流在遍历时包含与给定谓词匹配的初始流的元素。管道源的遍历直到管道的终端操作被执行后才开始。

在任何情况下,您都应该使用适当的方法来避免此类错误;forEach应该使用而不是在map这里,因为Stream#map它用于将流转换为对每个元素调用映射函数的结果,而Stream#forEach用于对其进行迭代。

演示:https : //ideone.com/ZQhLJC

strings
  .stream()
  .filter(x -> !distinct.add(x))
  .forEach(extras::add);
Run Code Online (Sandbox Code Playgroud)

另一种可能的解决方法是执行终端操作,例如.collect强制应用过滤器和映射。

strings
  .stream()
  .filter(x -> !distinct.add(x))
  .map(extra -> extras.add(extra)).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

如果要使用.collect,不妨使用收集的列表extras,以免浪费时间和空间。

List<String> extras = strings
  .stream()
  .filter(x -> !distinct.add(x)).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)


And*_*cus 5

您的代码不起作用,因为流没有被消耗。您只提供了中间操作,但在您调用像forEachreduce或等终止操作之前collect,您在流中定义的任何内容都不会被调用。

您应该使用peek打印通过流collect的元素并获取列表中的所有元素:

List<String> extras = strings
    .stream()
    .filter(x -> !distinct.add(x))
    .peek(System.out::println)
    .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

使用forEach填补之前创建的空集是一个代码的气味,没有任何函数式编程。