根据 OCP 书籍,必须避免有状态操作,也称为有状态 lambda 表达式。书中提供的定义是“有状态的 lambda 表达式是其结果取决于管道执行期间可能发生变化的任何状态的表达式。”
他们提供了一个示例,其中并行流用于使用 .map()
函数将固定的数字集合添加到同步的 ArrayList。
arraylist 中的顺序是完全随机的,这应该让人看到有状态的 lambda 表达式在运行时会产生不可预测的结果。这就是为什么强烈建议在使用并行流时避免有状态操作,以消除任何潜在的数据副作用。
它们没有显示无状态 lambda 表达式来解决相同的问题(将数字添加到同步数组列表),我仍然不明白使用映射函数填充空同步数组列表的问题是什么有数据......在管道执行期间可能改变的状态到底是什么?他们指的是 Arraylist 本身吗?就像另一个线程决定将其他数据添加到 ArrayList 而并行流仍在添加数字并因此改变最终结果的过程中一样?
也许有人可以为我提供一个更好的例子来说明什么是有状态的 lambda 表达式以及为什么应该避免使用它。将不胜感激。
谢谢
最佳答案
第一个问题是这样的:
List<Integer> list = new ArrayList<>();
List<Integer> result = Stream.of(1, 2, 3, 4, 5, 6)
.parallel()
.map(x -> {
list.add(x);
return x;
})
.collect(Collectors.toList());
System.out.println(list);
您不知道这里的结果是什么,因为您正在向非线程安全集合 ArrayList
添加元素。
但即使你这样做:
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
并执行相同的操作 list
没有可预测的顺序。多个线程添加到此同步集合。通过添加同步集合,您可以保证添加了所有元素(与普通的ArrayList
不同),但是它们将按哪个顺序出现在未知。
请注意,list
没有任何顺序保证,这称为处理顺序。虽然 result
保证为:对于这个特定示例,[1, 2, 3, 4, 5, 6]
。
根据问题的不同,您通常可以摆脱stateful
操作;对于返回 synchronized List
的示例,将是:
Stream.of(1, 2, 3, 4, 5, 6)
.filter(x -> x > 2) // for example a filter is present
.collect(Collectors.collectingAndThen(Collectors.toList(),
Collections::synchronizedList));
关于java - 有状态和无状态 lambda 表达式有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45052161/