如何在Java Stream中增加一个值?

时间:2016-10-28 12:12:07

标签: java dictionary java-8 java-stream

我想在python -m flake8的每次迭代中增加index的值。在1轻松实现。变量for-loopimage的数组。

这是我的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

2 个答案:

答案 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),否则这将是无用的......