Java流无干扰和副作用

Java流无干扰和副作用,java,java-stream,Java,Java Stream,查看Java.util.stream包文档时,对在streams使用中遵循的最佳实践产生了疑问。 考虑到这一准则: 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) {

查看Java.util.stream包文档时,对在streams使用中遵循的最佳实践产生了疑问。 考虑到这一准则:

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);
        }
});
HashMap map=newhashmap();
图.put(1,1);
map.put(2,2);
图.put(3,3);
图.put(4,4);
map.keySet().parallelStream().forEach(键->{
如果(键==3){
map.put(3,0);
}
});
  • 代码输出是否总是等于([1,1]、[2,2]、[3,0]、[4,4])
  • map.put(3,0)是否可以视为无干扰操作
  • map.put(3,0)能被认为是可接受的副作用吗

  • 换句话说,可以认为上述代码符合streams文档中建议的最佳实践?

    您的示例肯定违反了以下要求:

    对于大多数数据源,防止干扰意味着确保数据源在流管道执行期间根本不被修改。值得注意的例外是,其源是并发集合的流,专门设计用于处理并发修改

    您的数据源,
    HashMap
    ,不是为处理并发修改而设计的,因此在流管道的执行过程中根本不应该修改它

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

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

    否,否。
    避免副作用。
    符合文档要求的示例代码:

    Map<Integer,Integer> updatedMap = map.keySet().parallelStream()
            .filter(e -> e == 3)
            .collect(Collectors.toMap(Function.identity(), e -> 0));
    map.putAll(updatedMap);
    
    Map updatemap=Map.keySet().parallelStream()
    .过滤器(e->e==3)
    .collect(Collectors.toMap(Function.identity(),e->0));
    map.putAll(updatemap);
    比较(a)

    (添加了一个新条目)

    连同(b)

    (未创建、移动任何条目对象。但请注意
    LinkedHashMap

    • (a) 不安全
    • (b) 安全的

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

      (a) 否(b)是

    • map.put(3,0)
      是否可以视为无干扰操作

      (a) 否(b)
      setValue(0)
      yes

    • map.put(3,0)
      是否可以被视为可接受的副作用

      (a) 否(b)
      setValue(0)
      yes

    所以(a)是邪恶的,(b)是正确的

    为什么要提到entrySet.setValue

    实际上,
    HashMap.put
    在Oracle实现中的作用可能与
    Entry.setValue
    相同。这将需要使用实现知识——丑陋


    然而Entry.setValue是基于原始映射的支持,可以推断只有value字段被覆盖。请注意,
    LinkedHashMap
    需要对条目进行重新排序,而重新排序又是不安全的。

    这是重复的吗?如果可以多次中断代码,您就这样做了。您通过修改流的源违反了约定,并且通过从任意未知线程修改非线程安全的
    HashMap
    违反了约定。此外,您希望输出是
    [1,1]、[2,2]、[3,0]、[4,4]
    ,尽管
    HashMap
    不能保证任何顺序,因此即使没有其他违反合同的情况,您也不能想当然地假设该结果。最后,您要问的是,与记录的约束相反的操作是否“可以被视为符合最佳实践”…这仍然是偶然得到的结果。只有一个线程在修改映射,但它正在修改映射,而其他线程正在读取/迭代映射。可能的结果集仍然要大得多,包括获取异常或无限循环。这在很大程度上取决于
    HashMap
    的实现细节,例如,这四个条目在具有默认容量的
    HashMap
    中的分布不是非常适合并行的。此外,必须强调的是,期望
    HashMap
    的特定输出是错误的,即使在顺序情况下也是错误的,因为条目的顺序无法保证。请注意,
    LinkedHashMap
    默认情况下不会对
    Entry.setValue
    重新排序,只有在构造函数中指定了访问时重新排序。除此之外,没有为
    HashMap
    指定线程安全保证,即使您只使用
    Entry.setValue
    。感谢LinkedHashMap。关于HashMap pure:没有理由假设除了值字段上的并发访问之外还有其他问题:对于setValue,映射不相关,条目也不会移动。作为可能的基类,它不能保证任何东西。
    map.keySet().parallelStream().forEach(key -> {
            if (key == 3) { 
                map.put(3, 0);
            }
    });
    
    map.entrySet().parallelStream().forEach(e -> {
            if (e.getKey() == 3) { 
                e.setValue(0);
            }
    });