rak*_*ugi 5 java collections setter java-8 java-stream
我与同事讨论我们不应该stream.map()像这里建议的解决方案那样在内部使用 setter - /sf/answers/2476450441/
这个答案有一个评论不鼓励使用map这种方式,但没有给出为什么这是一个坏主意的原因。有人可以提供一个可能的场景,为什么这会中断?
我已经看到一些讨论,人们通过添加或删除项目来讨论集合本身的并发修改,但是使用map仅将一些值设置为数据对象有什么负面影响吗?
map像调用 setter 一样使用副作用,与peek用于非调试目的有很多相似之处,在 Java 流中讨论过的是否真的只用于调试?
这个答案有一个很好的一般建议:
不要以无意的方式使用 API,即使它实现了您的近期目标。这种方法在未来可能会中断,未来的维护者也不清楚。
而其他答案名称与实际问题相关;我必须引用自己:
您必须了解的重要一点是,流是由终端操作驱动的。终端操作确定是否必须处理所有元素或根本不处理任何元素。
当您将具有副作用的操作放入map函数时,您对它将在哪些元素上执行,甚至可能如何执行,例如以何种顺序执行,有特定的期望。预期是否会实现,取决于其他后续的 Stream 操作,甚至可能取决于细微的实现细节。
展示一些例子:
IntStream.range(0, 10) // outcome changes with Java 9
.mapToObj(i -> System.out.append("side effect on "+i+"\n"))
.count();
Run Code Online (Sandbox Code Playgroud)
IntStream.range(0, 2) // outcome changes with Java 10 (or 8u222)
.flatMap(i -> IntStream.range(i * 5, (i+1) * 5 ))
.map(i -> { System.out.println("side effect on "+i); return i; })
.anyMatch(i -> i > 3);
Run Code Online (Sandbox Code Playgroud)
IntStream.range(0, 10) // outcome may change with every run
.parallel()
.map(i -> { System.out.println("side effect on "+i); return i; })
.anyMatch(i -> i > 6);
Run Code Online (Sandbox Code Playgroud)
此外,正如在链接的答案中已经提到的,即使您有一个处理所有元素并已排序的终端操作,也无法保证中间操作的处理顺序(或并行流的并发性)。
当你有一个没有重复的流和一个处理所有元素的终端操作和一个map只调用一个简单的 setter的函数时,代码可能会做你想要的事情,但是代码对微妙的周围条件有很多依赖,它会变成维护噩梦。这让我们回到关于以非预期方式使用 API 的第一句话。