[英]Lambda in Stream.map/filter not called
I'm trying to find separate the duplicates and non-duplicates in a List
by adding them to a Set
and List
while using Stream.filter
and Stream.map
我试图Stream.filter
List
Set
Stream.map
List
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));
At the end of this, I expect distinct
to be [foo, bar, baz]
and extras
to be [foo, foo, bar]
, since there are 2 extra instances of foo
and 1 of bar
.最后,我希望distinct
是[foo, bar, baz]
和extras
是[foo, foo, bar]
,因为有foo
的 2 个额外实例和bar
的 1 个实例。 However, they are both empty after I run this.但是,在我运行它之后它们都是空的。
The lambdas given to the stream are never being called, which I verified by trying to print inside map
:从未调用 stream 的 lambda,我通过尝试在map
内打印来验证这一点:
.map(extra -> {
System.out.println(extra);
return extras.add(extra);
})
This doesn't work when I try to use put
with Map
either.当我尝试将put
与Map
一起使用时,这也不起作用。 What am I doing wrong?我究竟做错了什么?
Note: There may be other questions similar to this, but I'm looking for a kind of canonical answer for why this sort of stuff doesn't work with Java 8's Streams.注意:可能还有其他类似的问题,但我正在寻找一种规范的答案来解释为什么这类东西不适用于 Java 8 的 Streams。 If you can make this a more general question (even if it means completely changing it), I'd appreciate it.如果你能把这个问题变成一个更普遍的问题(即使这意味着完全改变它),我会很感激的。
Both Stream#filter
and Stream#map
are intermediate operations, which means that they are evaluated lazily. Stream#filter
和Stream#map
都是中间操作,这意味着它们是惰性求值的。 According to the documentation:根据文档:
Intermediate operations return a new stream.中间操作返回一个新的 stream。 They are always lazy;他们总是很懒惰; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate.执行诸如 filter() 之类的中间操作实际上并没有执行任何过滤,而是创建了一个新的 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ,当被遍历时,它包含与给定谓词匹配的初始 stream 的元素。 Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.直到管道的终端操作被执行,管道源的遍历才开始。
In any case, you should be using the appropriate methods to avoid errors like this;无论如何,您应该使用适当的方法来避免这样的错误; forEach
should be used instead of map
here as Stream#map
is used to convert the stream to the result of calling the mapping function on each element, while Stream#forEach
is used to iterate over it.此处应使用forEach
代替map
,因为Stream#map
用于将 stream 转换为调用映射 function 对每个元素进行迭代的结果,而使用Stream#forEach
对其进行迭代。
Demo: https://ideone.com/ZQhLJC演示: https://ideone.com/ZQhLJC
strings
.stream()
.filter(x -> !distinct.add(x))
.forEach(extras::add);
Another possible workaround is to perform a terminal operation like .collect
to force the filter and map to be applied.另一种可能的解决方法是执行终端操作,如.collect
以强制应用过滤器和 map。
strings
.stream()
.filter(x -> !distinct.add(x))
.map(extra -> extras.add(extra)).collect(Collectors.toList());
If you are going to use .collect
, you might as well use the collected list as extras
to avoid wasting time and space.如果要使用.collect
,不妨将收集的列表用作extras
项,以免浪费时间和空间。
List<String> extras = strings
.stream()
.filter(x -> !distinct.add(x)).collect(Collectors.toList());
Your code does not work, because the stream is not consumed.您的代码不起作用,因为没有消耗 stream。 You have provided only the intermediate operations, but until you call a terminating operation like forEach
, reduce
or collect
, nothing you have defined in your stream will be invoked.您只提供了中间操作,但在您调用诸如forEach
、 reduce
或collect
之类的终止操作之前,您在 stream 中定义的任何内容都不会被调用。
You should rather use peek
to print the elements going through the stream and collect
to get all the elements in the list:您应该使用peek
打印通过 stream 的元素并collect
以获取列表中的所有元素:
List<String> extras = strings
.stream()
.filter(x -> !distinct.add(x))
.peek(System.out::println)
.collect(Collectors.toList());
Using forEach
to fill the empty collection created before is a code smell and has nothing to do with functional programming.使用forEach
填充之前创建的空集合是代码异味,与函数式编程无关。
In order to the filter to be applied, you need to call a terminal operation like collect() .为了应用过滤器,您需要调用像collect()这样的终端操作。 In that case you can assign the items which pass the filter directly to the extras list instead of use map function.在这种情况下,您可以将通过过滤器的项目直接分配给附加列表,而不是使用map function。
Try something like this:尝试这样的事情:
List<String> strings = Arrays.asList("foo", "bar", "foo", "baz", "foo", "bar");
Set<String> distinct = new HashSet<>();
List<String> extras = strings
.stream()
.filter(x -> !distinct.add(x))
.collect(Collectors.toList());
There is a more elegant way to use the filter
with Predicate negate()
method instead of using logical operator !
有一种更优雅的方法可以使用 Predicate negate()
方法而不是使用逻辑运算符来使用filter
!
List<String> extras = strings
.stream()
.filter(((Predicate<String>) distinct::add).negate())
.peek(System.out::println)
.collect(Collectors.toList());
peek
is a function used for pipeline debugging only. peek
是一个 function 仅用于流水线调试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.