Java 8 Lambda闭包在不断变化的集合上



我想向ExecutiorService提交一个lambda。lambda正在接近一个累加的Arraylist,但我使用";toArray;然后清除蓄能器。λ似乎没有捕捉到"0"的响应;toArray";。

但是,如果我将副本分配给lambda之外的另一个引用,那么它就得到了正确的处理。

原始代码:

public class Test {
public static void main(String[] args) {
final int BATCH = 10;
ArrayList<Integer> accumulator = new ArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < BATCH * 10; i++) {
accumulator.add(i);
if (accumulator.size() >= BATCH) {
executorService.submit(() -> run(accumulator.toArray())); // faulty
accumulator.clear();
}
}
executorService.shutdown();
try {
if (!executorService.awaitTermination(10, TimeUnit.MINUTES)) {
System.err.println("Waited for 10 minutes, forced exit");
System.exit(0);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static void run(Object[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}

上面没有打印出";i〃;,我猜,当lambda的RHS被求值为调用accumulator.toArray((时,accumulator已经更改。

现在,如果我将对副本的引用传递给lambda,它就可以工作了。

final Object[] temp = accumulator.toArray();
executorService.submit(() -> run(temp));
accumulator.clear();

编辑:

我感兴趣的是对这种行为的解释?我的错误是因为lambda右侧的函数调用(toArray(只有在执行完整lambda时才会求值吗?

因为它是lambda,所以对accumulator.toArray()的调用将推迟到lambda实际执行为止。到那时,名单本可以被清除。

一个非最终局部变量,其值在初始化后从未更改,称为"有效最终"。引入这个概念是因为在Java8之前,我们不能在匿名类中使用非最终局部变量。如果您有权访问Anonymous类中的局部变量,则必须使其成为最终变量。

Lambdas推出后,这一限制有所放宽。因此,如果局部变量在初始化后没有更改,则需要使其成为final,因为Lambda本身就是一个匿名类。Java 8意识到了每次开发人员使用Lambda时都要声明局部变量final的痛苦,并引入了这一概念,使局部变量不必成为final。所以,如果你看到匿名类的规则仍然没有改变,那只是你不必每次使用lambdas时都写final关键字。

换句话说,您不能直接传递accumulator变量,因为您试图在它完成后对其进行变异。

最新更新