Java Streams - 在 map() 中使用 setter

rak*_*ugi 5 java collections setter java-8 java-stream

我与同事讨论我们不应该stream.map()像这里建议的解决方案那样在内部使用 setter - /sf/answers/2476450441/

这个答案有一个评论不鼓励使用map这种方式,但没有给出为什么这是一个坏主意的原因。有人可以提供一个可能的场景,为什么这会中断?

我已经看到一些讨论,人们通过添加或删除项目来讨论集合本身的并发修改,但是使用map仅将一些值设置为数据对象有什么负面影响吗?

Hol*_*ger 5

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 的第一句话。