lambda中的Java 8 lambda无法修改外部lambda中的变量 [英] Java 8 lambda within a lambda can't modify variable from outer lambda
问题描述
假设我有列表< String>
和列表< Transfomer>
。我想将每个转换器应用于列表中的每个字符串。
Suppose I have a List<String>
and a List<Transfomer>
. I want to apply each transformer to each string in the list.
使用Java 8 lambdas,我可以这样做:
Using Java 8 lambdas, I can do this:
strings.stream().map(s -> {
for(Transformer t : transformers) {
s = t.apply(s);
}
return s;
}).forEach(System.out::println);
但是我想做更像这样的事情,但是会导致编译时错误:
But I'd like to do something more like this, however it results in a compile time error:
strings.stream().map(s -> transformers.stream().forEach(t -> s = t.apply(s))).forEach(System.out::println);
我刚开始玩lambdas,所以也许我只是没有正确的语法。
I'm just starting to play with lambdas, so maybe I just don't have the syntax correctly.
推荐答案
使用stream执行此操作的最佳方法是使用 reduce
:
The best way to do this with streams is to use reduce
:
// make a transformer that combines all of them as one
Transformer combinedTransformer =
// the stream of transformers
transformers.stream()
// combine all the transformers into one
.reduce(
// apply each of the transformers in turn
(t1, t2) -> x -> t2.apply(t1.apply(x)))
);
// the stream of strings
strings.stream()
// transform each string with the combined transformer
.map(combinedTranformer::apply);
当然,这假设变形金刚
是非空;如果它有可能是空的,那么使用 reduce
的双参数重载就足够了,就像这样(假设 Tranformer
是一个功能界面):
Of course, this assumes that transformers
is non-empty; if there is a possibility that it is empty, than it is simple enough to use the two-argument overload of reduce
instead, like so (this assumes Tranformer
is a functional interface):
// make a transformer that combines all of them as one
Transformer combinedTransformer =
// the stream of transformers
transformers.stream()
// combine all the transformers into one
.reduce(
// the no-op transformer
x -> x,
// apply each of the transformers in turn
(t1, t2) -> x -> t2.apply(t1.apply(x)))
);
// the stream of strings
strings.stream()
// transform each string with the combined transformer
.map(combinedTranformer::apply);
您遇到编译器错误的原因是,正如错误所说,lambda表达式中使用的外部变量必须有效最终;也就是说,声明它们 final
(如果它们还没有)不得改变程序的含义,或改变它是否编译。因此,在lambda中使用可变赋值通常是被禁止的,并且有充分理由:突变会导致并行化,并且Java 8中包含lambda的主要原因之一是允许更容易的并行编程。
The reason you got a compiler error is that, as the error says, outside variables used in a lambda expression must be effectively final; that is, declaring them final
(if they aren't already) must not change the meaning of the program, or change whether or not it compiles. Using a mutable assignment in a lambda is therefore generally forbidden, and with good reason: mutation screws up parallelization, and one of the major reasons lambdas were included in Java 8 was to allow easier parallel programming.
一般来说,每当你想以某种方式总结结果时, reduce
(在其三个重载中的任何一个)都是你的首选方法。学习如何使用 map
,过滤器
, reduce
,以及使用 Stream
s时, flatMap
非常重要。
Generally speaking, whenever you want to "sum up" results in some way, reduce
(in any of its three overloads) is your go-to method. Learning how to use map
, filter
, reduce
, and flatMap
effectively is very important when working with Stream
s.
这篇关于lambda中的Java 8 lambda无法修改外部lambda中的变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!