java - Java Streams - 在 map() 中使用 setter
问题描述
我与同事讨论过,我们不应该stream.map()
像这里建议的解决方案那样在内部使用设置器 - https://stackoverflow.com/a/35377863/1552771
对此答案有一条评论不鼓励使用map
这种方式,但没有给出理由说明为什么这是一个坏主意。有人可以提供一个可能的情况,为什么这会中断?
我已经看到一些讨论,人们谈论通过添加或删除项目来同时修改集合本身,但是使用map
只是为数据对象设置一些值有什么负面影响吗?
解决方案
在调用 setter 时使用副作用与将其用于非调试目的map
有很多相似之处,在 Java 流中已经讨论过这些副作用真的只用于调试吗?peek
这个答案有一个很好的一般建议:
不要以非预期的方式使用 API,即使它实现了您的直接目标。这种方法将来可能会失效,未来的维护者也不清楚。
而其他答案命名相关的实际问题;我必须引用自己:
您必须了解的重要一点是,流是由终端操作驱动的。终端操作确定是否必须处理所有元素或任何元素。
当您将具有副作用的操作放入map
函数中时,您对将执行哪些元素甚至可能如何执行(例如以何种顺序)有一个特定的期望。期望是否会实现,取决于其他后续 Stream 操作,甚至可能取决于细微的实现细节。
举一些例子:
IntStream.range(0, 10) // outcome changes with Java 9
.mapToObj(i -> System.out.append("side effect on "+i+"\n"))
.count();
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);
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);
此外,正如链接答案中已经提到的,即使您有一个处理所有元素并已排序的终端操作,也不能保证中间操作的处理顺序(或并行流的并发性)。
当你有一个没有重复的流和一个处理所有元素的终端操作和一个map
只调用一个微不足道的 setter 的函数时,代码可能会碰巧做你想要的事情,但是代码对微妙的环境条件有太多的依赖关系,以至于它会变成维护的噩梦。这让我们回到了关于以非预期方式使用 API 的第一句话。
推荐阅读
- swift - SwipeCellKit 删除 tableView 中的最后一行创建错误
- c# - C# ASP.NET 表单查询:如何将复选框列表中的多个列表项获取到 SQL 查询中以输出到 Gridview
- c# - 使用 C# 静默卸载 InstallShield Installscript MSI 程序
- javascript - 反应原生组件之间的导航
- java - JPA 和 Hibernate 缺少 INNER JOIN ON 子句
- reactjs - 在没有 setState 的情况下反应状态数组对象的变化
- r - 计算R中数据框中每个组的总和和最小值最大值
- sql-server - SQL Server - 比较 2 个表中存在于相同列中的数据而不检查是否相等
- android - Firebase - 如何结构化数据
- javascript - Cloud Firestore 文档 .get() 和 .where() 函数无法识别