查看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);
}
});
换句话说,以上代码可以被视为符合流文档中建议的最佳实践吗?
答案 0 :(得分:6)
您的示例绝对违反了non interference requirement:
对于大多数数据源,防止干扰意味着确保在流管道执行期间根本不修改数据源。值得注意的例外是流的源是并发集合,这些流是专门为处理并发修改而设计的。
您的数据源HashMap
不能处理并发修改,因此,在执行流管道时完全不应对其进行修改。
因此,您的第二个和第三个问题的答案是否定的。
对于第一个问题,由于您的条件确保只有一个线程将调用map.put(3,0)
,因此您的特定代码仍可能产生预期的结果。但是,这仍然被认为是Stream
的不正确用法。
答案 1 :(得分:1)
不,不,不。
避免副作用。
符合文档的示例代码:
Map<Integer,Integer> updatedMap = map.keySet().parallelStream()
.filter(e -> e == 3)
.collect(Collectors.toMap(Function.identity(), e -> 0));
map.putAll(updatedMap);
答案 2 :(得分:1)
比较(a)
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
。)
(b)安全
代码输出是否始终等于([1,1],[2,2],[3,0],[4,4])?
(a)否(b)是
map.put(3, 0)
是否可以视为无干扰操作?
(a)否(b)setValue(0)
是
map.put(3, 0)
是否可以视为可接受的副作用?
(a)否(b)setValue(0)
是
所以(a)是邪恶的,(b)是好的。
为什么要提到entrySet.setValue?
实际上 HashMap.put
在Oracle 实现中可能与Entry.setValue
相同。那将需要使用实施知识-很难。
鉴于Entry.setValue基于原始地图的支持,因此可能会推断出只有value字段被覆盖。请注意,LinkedHashMap
需要对条目进行重新排序,并且重新排序也不安全。