如何在Java Stream中增加值?

我希望将每次迭代的index值增加1 。 很容易在for-loop 。 变量imageImageView的数组。

这是我的for-loop

 for (Map.Entry entry : map.entrySet()) { image[index].setImage(entry.getValue().getImage()); index++; } 

为了练习Stream,我尝试将其重写为Stream

 map.entrySet().stream() .forEach(e -> item[index++].setImage(e.getValue().getImage())); 

导致我错误的原因:

错误:从lambda表达式引用的局部变量必须是final或者有效的final

如何重写Stream递增要使用的变量index

你不应该。 这两个看起来很相似,但它们在概念上是不同的。 循环只是一个循环,但是forEach指示库对每个元素执行操作, 既不指定操作的顺序(对于并行流),也不指定将执行它们的线程 。 如果你使用forEachOrdered ,那么仍然没有关于线程的保证,但至少你可以保证在后续元素上的动作之间发生关系。

特别注意文档说:

对于任何给定元素,可以在任何时间以及库选择的任何线程中执行该动作。 如果操作访问共享状态,则它负责提供所需的同步。

正如@Marko在下面的评论中指出的那样,它只适用于并行流,即使措辞有点令人困惑。 然而,使用循环意味着您甚至不必担心所有这些复杂的东西!

所以底线是:如果逻辑是它所在函数的一部分,则使用循环,如果你只想告诉Java“对流的元素执行此操作”,则使用forEach

那是关于forEach vs循环。 现在讨论为什么变量首先必须是最终的,以及为什么你可以对类字段和数组元素这样做。 这是因为,就像它说的那样,Java有一个限制,即匿名类和lambdas不能访问局部变量,除非它永远不会改变。 意思不仅是他们自己不能改变它,而且你也不能在它们之外改变它。 但这仅适用于局部变量,这就是为什么它适用于其他一切,如类字段或数组元素。

我认为,这种限制的原因是终身问题。 仅当包含它的块正在执行时,才存在局部变量。 由于垃圾收集,所有其他东西都存在,而有引用它。 其他一切都包括lambdas和匿名类,所以如果他们可以修改具有不同生命周期的局部变量,那么可能会导致类似于C ++中悬空引用的问题。 所以Java采取了简单的方法:它只是在创建lambda / anonymous类时复制局部变量。 但是,如果你可以改变那个变量,那将导致混乱(因为副本不会改变,因为副本是不可见的,所以会非常混乱)。 所以Java只是禁止对这些变量进行任何更改,就是这样。

关于最终变量和已经讨论过的匿名类有很多问题,比如这个 。

虽然标准的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 ),否则这将是无用的……