【问题标题】:Using java streams to put the last encountered value into a map使用 java 流将最后遇到的值放入映射中
【发布时间】:2017-02-08 15:36:59
【问题描述】:

我有一些代码如下:

Map<RiskFactor, RiskFactorChannelData> updateMap =
    updates.stream().filter(this::updatedValueIsNotNull). // Remove null updated values
        collect(Collectors.toMap(
            u -> u.getUpdatedValue().getKey(), // then merge into a map of key->value.
            Update::getUpdatedValue,
            (a, b) -> b)); // If two values have the same key then take the second value

具体来说,我想从列表中获取值并将它们放入地图中。这一切都完美无缺。不过我担心的是订购。

例如,如果列表有:

a1, b1, a2

如何确保最终地图包含:

a->a2
b->b1

而不是

a->a1
b->b1

传入的列表是有序的,stream().filter() 应该保持顺序,但我在Collectors.toMap 的文档中看不到任何关于输入排序的内容。

这在一般情况下是安全的,还是到目前为止我的测试用例很幸运?我是否会依赖 JVM 并在未来面临这种变化的风险?

如果我只写一个 for 循环,这很容易保证,但潜在流行为的“模糊性”让我担心。

我不打算为此使用并行,我只是想了解在达到toMap 的顺序非并行流的情况下的行为。

【问题讨论】:

标签: java java-stream


【解决方案1】:

“最新值”一词有点误导。既然要按照遇到顺序的最后一个值,答案是toMap会尊重遇到顺序。

Its documentation 引用Map.merge 来解释合并函数的语义,但不幸的是,该文档也有点薄。它没有提到这个函数是用(oldValue,newValue) 显式调用的;只能从代码示例中推导出来。

toMap’s documentation 进一步声明:

返回的Collector 不是并发的。对于并行流管道,combiner 函数通过将键从一个映射合并到另一个映射来操作,这可能是一项昂贵的操作。如果不需要将结果按遇到顺序合并到Map 中,使用toConcurrentMap(Function, Function, BinaryOperator, Supplier) 可能会提供更好的并行性能。

因此,如果不需要遇到顺序,它会显式指向不同的收集器。一般来说,Collectors 提供的所有内置收集器都是无序的,如果明确说明的话,只有“...并发...”收集器和toSet() 收集器的情况。

【讨论】:

    【解决方案2】:

    这是安全的,Collection.stream() 创建一个顺序流。

    我建议查看Collectors.toMap,以防发生冲突,注意选择正确的值。在您的情况下,您应该使用更新的。

    你感兴趣的部分是(a, b) -&gt; b,你可以随意选择第二个元素,那里应该选择最近的。

    我认为您的问题来自不确定处理顺序的事实,如果您想继续使用流(而不是 for 循环),您可以强制此状态在 .sequential() 之后添加 .stream()

    我更喜欢另一种方式,是向RiskFactorChannelData 添加时间戳,甚至使用并行流。

    【讨论】:

    • 对,但我知道最近的唯一方法是通过它在原始数组中的位置。
    • @TimB 从数组构建的流按数组的顺序遍历,我认为这是一个很好的答案。
    • @TimB,在这种情况下,我想你不能将流用于你真正想要的东西。想象一下自己将parallelStream 放在那里,以加快速度或其他任何事情 - 这意味着您的流变得无序。我建议你做的是在你的更新中添加一个时间戳之类的东西来表示它的实际顺序,所以你不依赖数组位置。
    • @Eugene,是的,暗示流遍历将按数组顺序进行。但如果他在未来的某个时候与它平行,它就完全崩溃了。总而言之,如果他真正想要的是简单的 for 循环,我认为在那里使用流没有任何好处。
    • @M. Prokhorov:这些块将填充不同的映射,这就是为什么收集器有一个 Supplier 而不是单个 Map 实例。考虑到块的原始遭遇顺序,部分结果随后合并到单个地图中。
    猜你喜欢
    • 2022-11-01
    • 2023-03-15
    • 2013-05-04
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多