为了账号安全,请及时绑定邮箱和手机立即绑定

Java 流无干扰和副作用

Java 流无干扰和副作用

慕的地10843 2021-12-01 16:56:36
查看 Java java.util.stream 包文档,对于在流使用中遵循的最佳实践产生了疑问。考虑到这段代码:HashMap<Integer,Integer> map = new HashMap<>();map.put(1,1);map.put(2,2);map.put(3,3);map.put(4,4);map.keySet().parallelStream().forEach(key -> {        if (key == 3) {             map.put(3,0);        }});代码输出是否总是等于 ([1,1],[2,2],[3,0],[4,4])?map.put(3,0) 可以被视为非干扰操作吗?map.put(3,0) 可以被视为可接受的副作用吗?换句话说,上面的代码可以被认为符合流文档中建议的最佳实践吗?
查看完整描述

3 回答

?
不负相思意

TA贡献1777条经验 获得超10个赞

不不不。

避免副作用。

符合文档的示例代码:


Map<Integer,Integer> updatedMap = map.keySet().parallelStream()

        .filter(e -> e == 3)

        .collect(Collectors.toMap(Function.identity(), e -> 0));

map.putAll(updatedMap);


查看完整回答
反对 回复 2021-12-01
?
梦里花落0921

TA贡献1772条经验 获得超6个赞

您的示例绝对违反了非干扰要求

对于大多数数据源来说,防止干扰意味着确保数据源在流管道的执行过程中完全不被修改。一个显着的例外是其源是并发集合的流,这些流是专门为处理并发修改而设计的。

您的数据源 aHashMap并非旨在处理并发修改,因此在流管道执行期间根本不应修改它。

因此,您的第二和第三个问题的答案是否定的。

至于第一个问题,您的特定代码可能仍会产生预期的结果,因为您的条件确保只有一个线程会调用map.put(3,0). 但是,这仍然被认为是Streams的错误用法。


查看完整回答
反对 回复 2021-12-01
?
LEATH

TA贡献1936条经验 获得超6个赞

比较(一)


map.keySet().parallelStream().forEach(key -> {

        if (key == 3) { 

            map.put(3, 0);

        }

});

(添加了一个新条目)


与 (b)


map.entrySet().parallelStream().forEach(e -> {

        if (e.getKey() == 3) { 

            e.setValue(0);

        }

});

(没有创建、移动 Entry 对象。但要注意LinkedHashMap.)

  • (a) 不安全

  • (b) 安全

    1. 代码输出是否总是等于 ([1,1],[2,2],[3,0],[4,4])?

      (a) 否 (b) 是

    2. 可以map.put(3, 0)认为是非干扰操作吗?

      (a) 否 (b)setValue(0)

    3. 可以map.put(3, 0)认为是可以接受的副作用吗?

      (a) 否 (b)setValue(0)

所以(a)是邪恶的,(b)是好的。

为什么要提到 entrySet.setValue?

实际上 HashMap.put在 Oracle实现中可能与Entry.setValue. 这将需要使用实现知识 - 丑陋。

而 Entry.setValue 基于原始地图的支持,人们可能会推断出只有 value 字段被覆盖。请注意, aLinkedHashMap需要对条目重新排序,而重新排序又是不安全的。


查看完整回答
反对 回复 2021-12-01
  • 3 回答
  • 0 关注
  • 213 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信