java - 为什么不推荐基于 AtomicInteger 的 Stream 解决方案?

标签 java java-8 thread-safety java-stream atomicinteger

假设我有这份水果 list :-

List<String> f = Arrays.asList("Banana", "Apple", "Grape", "Orange", "Kiwi");

我需要在每个水果前添加一个序列号并打印出来。水果或序列号的顺序无关紧要。所以这是一个有效的输出:-
4. Kiwi
3. Orange
1. Grape
2. Apple
5. Banana

解决方案 #1
AtomicInteger number = new AtomicInteger(0);

String result = f.parallelStream()
        .map(i -> String.format("%d. %s", number.incrementAndGet(), i))
        .collect(Collectors.joining("\n"));

解决方案#2
String result = IntStream.rangeClosed(1, f.size())
        .parallel()
        .mapToObj(i -> String.format("%d. %s", i, f.get(i - 1)))
        .collect(Collectors.joining("\n"));

问题

为什么解决方案#1 是不好的做法?我在很多地方看到AtomicInteger基于的解决方案很糟糕(如 this answer ),特别是在并行流处理中(这就是我在上面使用并行流来尝试遇到问题的原因)。

我看了这些问题/答案:-
In which cases Stream operations should be stateful?
Is use of AtomicInteger for indexing in Stream a legit way?
Java 8: Preferred way to count iterations of a lambda?

他们只是提到(除非我错过了什么)“可能会出现意想不到的结果”。像什么?在这个例子中会发生吗?如果没有,你能给我提供一个可能发生的例子吗?

至于“不保证映射器函数的应用顺序”,嗯,这是并行处理的本质,所以我接受它,而且,在这个特定的例子中顺序并不重要。
AtomicInteger是线程安全的,因此在并行处理中应该不成问题。

有人可以提供示例,在哪些情况下使用这种基于状态的解决方案会出现问题?

最佳答案

好吧,看看斯图尔特·马克斯的回答是什么here - 他正在使用有状态谓词。

这是几个潜在的问题,但如果您不关心它们或真正理解它们 - 您应该没问题。

首先是顺序,在并行处理的当前实现下展示,但如果您不关心顺序,就像在您的示例中一样,您没问题。

第二个是潜在速度AtomicInteger如上所述,如果您关心这一点,那么增加一个简单的 int 会慢一些。

第三个更微妙。有时不能保证map将被执行,例如从 java-9 开始:

 someStream.map(i -> /* do something with i and numbers */)
           .count();

这里的重点是,由于您正在计数,因此无需进行映射,因此已跳过。通常,不保证命中某些中间操作的元素会到达终端。想象一个 map.filter.map在这种情况下,与第二个 map 相比,第一个 map 可能“看到”更多元素,因为某些元素可能会被过滤。所以不建议依赖这个,除非你能准确地推断出正在发生的事情。

在您的示例中,IMO,您可以安全地做您所做的事情;但是如果你稍微改变你的代码,这需要额外的推理来证明它的正确性。我会选择解决方案 2,因为它对我来说更容易理解,而且它没有上面列出的潜在问题。

关于java - 为什么不推荐基于 AtomicInteger 的 Stream 解决方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53329809/

相关文章:

java - 如何访问Eclipse插件的Eclipse运行配置的变量解析?

java - 在 Spring Rest Controller 中执行的方法明显比在纯 java 项目中慢

java - 对非 volatile 字段的同步访问是否安全?

c++ - 哪些是C++和Qt中的线程安全打印语法?

c# - 为什么调用空事件处理程序不会引发异常?

Java 8 Lambda : Mapping a Stream to type Integer and then calling sum() won't compile

java - Java try-catch-finally 中的奇怪错误

java - ZoneId 的 ID 的本地化名称

java - 在 postgres 中 "timestamp with time zone"类型的 Jooq 绑定(bind)

java - 为什么这个 Java 8 方法引用会编译?