假设我有这份水果清单:-
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
基于解决方案不好(比如在这个答案中),特别是在并行流处理中(这就是我在上面使用并行流来尝试遇到问题的原因)。
我查看了这些问题/答案:-
在哪些情况下流操作应该是有状态的?
使用 AtomicInteger 在 Stream 中进行索引是否合法?
Java 8:计算 lambda 迭代的首选方法?
他们只是提到(除非我错过了什么)“可能会出现意想不到的结果”。像什么?在这个例子中会发生吗?如果没有,你能给我一个可能发生的例子吗?
至于“不保证应用映射器函数的顺序”,嗯,这是并行处理的本质,所以我接受它,而且,在这个特定的例子中,顺序并不重要。
AtomicInteger
是线程安全的,因此在并行处理中应该不是问题。
有人可以提供示例,在哪些情况下使用这种基于状态的解决方案会出现问题?