Java流无干扰和副作用
查看Java.util.stream包文档时,对在streams使用中遵循的最佳实践产生了疑问。 考虑到这一准则: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) {
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);
}
});
换句话说,可以认为上述代码符合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);
}
});