我正在阅读理查德沃伯顿的书中的 Java 8 Lambdas 中的lambdas。他开始在现代CPU中使用并发性进行讨论,并最终将它与lambdas相关联。我不知道我错过了什么,但我肯定没有得到这个概念。考虑以下课程
class A {
private int state;
A(){
state = 0;
}
A(int state){
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
@Override
public String toString() {
return Integer.toString(state);
}
} // end A
public class Main {
public static void main(String[] args) {
List<A> ls = new ArrayList<>();
ls.add(new A(2));
ls.add(new A(3));
ls.forEach( a -> a.setState(a.getState() + 1) );
System.out.println(ls); // [3, 4]
} // end main
} // end class Main
此构造ls.forEach( a -> a.setState(a.getState() + 1) );
如何比
ls.forEach(new Consumer<A>() {
@Override
public void accept(A t) {
t.setState(t.getState() + 1);
}
});
答案 0 :(得分:5)
Lambdas可能并不反对匿名类,但内部迭代(使用forEach
)与外部迭代(使用增强的for
循环)相对。在这种情况下,它是有道理的。像这样的外部迭代无法并行化:
for(A a : ls) {
a.setState(a.getState() + 1);
}
相反,内部迭代不受限制:如果方法规范允许(例如,Stream.forEach()
),只要客户端代码遵守某些规则(例如,不修改共享状态),它就可以在不更改客户端代码的情况下进行并行化。从这个意义上说,内部迭代更加友好。
答案 1 :(得分:2)
从开发人员的角度来看,第一个lambda示例只是由java编译器扩展为您提供的第二个代码示例。当且仅当函数的参数需要具有一个未知方法的接口时,Lambdas基本上创建一个匿名内部类。
实际的Java8实现稍有不同,因为它们不想生成大量的类文件,因此实现类是在运行时通过在invokedynamic的帮助下绑定私有方法(lambda代码)来创建的。来自Goetz的一些注释也提到了运行时可能存在一些缓存优化机会。
理论上,具有库实现知识的优化编译器可以做一些聪明的事情,但这从未像java编译器那样工作,主要是因为java源编译器通常与运行时分离。 (invokedynamic部分确实提供了一些机会但不在库级别上)
然而,在风格上使用lambda语法更简洁,也可以被视为更干净的选项,因为您不会想要将变量插入到包含内部类的状态中。