我想在python -m flake8
的每次迭代中增加index
的值。在1
轻松实现。变量for-loop
是image
的数组。
这是我的ImageView
。
for-loop
为了练习Stream,我尝试将其重写为for (Map.Entry<String, Item> entry : map.entrySet()) {
image[index].setImage(entry.getValue().getImage());
index++;
}
:
Stream
导致错误:
错误:从lambda表达式引用的局部变量必须是最终的或有效的最终
如何重写map.entrySet().stream()
.forEach(e -> item[index++].setImage(e.getValue().getImage()));
增加要使用的变量Stream
?
答案 0 :(得分:6)
你不应该。这两个看起来很相似,但它们在概念上是不同的。循环只是一个循环,但是forEach
指示库对每个元素执行操作,而不指定操作顺序(对于并行流),也不指定将执行它们的线程 。如果您使用forEachOrdered
,那么仍然无法保证线程,但至少您可以保证在后续元素上的操作之间发生关系。
特别注意the docs说:
对于任何给定元素,可以在任何时间执行该动作 以及图书馆选择的任何线索。如果操作访问 共享状态,它负责提供所需的 同步。
正如@Marko在下面的评论中指出的那样,它只适用于并行流,即使措辞有点令人困惑。然而,使用循环意味着您甚至不必担心所有这些复杂的东西!
所以底线是:如果该逻辑是其所在函数的一部分,则使用循环,如果您只想告诉Java“对流的元素执行此操作”,请使用forEach
。
那是关于forEach
vs循环。现在讨论为什么变量首先需要最终的主题,以及为什么你可以对类字段和数组元素这样做。这是因为,就像它说的那样,Java有一个限制,即匿名类和lambdas不能访问局部变量,除非它永远不会改变。意思不仅是他们自己不能改变它,而且你也不能在它们之外改变它。但这只适用于局部变量,这就是为什么它适用于其他一切,如类字段或数组元素。
我认为,这种限制的原因是终身问题。只有在包含它的块正在执行时,才存在局部变量。由于垃圾收集,所以在存在引用的同时存在其他所有内容。其他一切都包括lambdas和匿名类,所以如果他们可以修改具有不同生命周期的局部变量,那么可能会导致类似于C ++中悬空引用的问题。所以Java采取了简单的方法:它只是在创建lambda / anonymous类时复制局部变量。但如果您可以更改该变量,那将导致混淆(因为副本不会更改,并且由于副本不可见,因此会非常混乱)。所以Java只是禁止对这些变量进行任何更改,就是这样。
关于最终变量和已经讨论过的匿名类,有许多问题,例如this one。
答案 1 :(得分:2)
虽然标准的Stream API缺少它,但某种“zip”操作在这里会有所帮助。一些扩展Stream API的第三方库提供了它,包括我的免费StreamEx库:
IntStreamEx.ints() // get stream of numbers 0, 1, 2, ...
.boxed() // box them
.zipWith(StreamEx.ofValues(map)) // zip with map values
.forKeyValue((index, item) -> image[index].setImage(item.getImage()));
有关详细信息,请参阅zipWith
文档。请注意,您的地图应该有有意义的顺序(例如LinkedHashMap
),否则这将是无用的......