[英]Is it safe to use parallelstream() to populate a Map in Java 8
我有一个包含100万个对象的列表,我需要将其填充到Map中。 现在,我想减少将其填充到Map中的时间,为此我计划使用Java 8 parallelstream(),如下所示:
List<Person> list = new LinkedList<>();
Map<String, String> map = new HashMap<>();
list.parallelStream().forEach(person ->{
map.put(person.getName(), person.getAge());
});
我想问一下,通过并行线程填充这样的Map是否安全。 难道不可能出现并发问题,并且某些数据可能会在Map中丢失吗?
使用parallelStream()
来收集 HashMap
是非常安全的。 但是,使用parallelStream()
, forEach
和消费者向HashMap
添加内容是不安全的。
HashMap
不是同步类,并且尝试同时将元素放入其中将无法正常工作。 这就是forEach
将要做的事情,它将调用给定的使用者,它可以同时从多个线程将元素放入HashMap
。 如果你想要一个简单的代码来证明这个问题:
List<Integer> list = IntStream.range(0, 10000).boxed().collect(Collectors.toList());
Map<Integer, Integer> map = new HashMap<>();
list.parallelStream().forEach(i -> {
map.put(i, i);
});
System.out.println(list.size());
System.out.println(map.size());
一定要运行几次。 操作后打印的地图大小不是10000,这是列表的大小,但稍微少一点,这是一个非常好的机会(并发的乐趣)。
这里的解决方案一如既往不是使用forEach
,而是使用collect
方法和内置toMap
的可变缩减方法:
Map<Integer, Integer> map = list.parallelStream().collect(Collectors.toMap(i -> i, i -> i));
使用在上面的示例代码行的代码,你可以放心,地图大小将始终是10000的流API确保它是安全的 ,收集到非线程安全的容器,即使是在平行。 这也意味着您不需要使用toConcurrentMap
是安全的,如果您特别想要ConcurrentMap
作为结果,而不是一般Map
,则需要此收集器; 但就线程安全而言,关于collect
,你可以使用两者。
HashMap
不是线程安全的,但是ConcurrentHashMap
是; 用它代替
Map<String, String> map = new ConcurrentHashMap<>();
并且您的代码将按预期工作。
forEach()
与toMap()
性能比较 在JVM预热后,使用1M元素,使用并行流和使用中值时序, forEach()
版本始终比toMap()
版本快2-3倍。
结果在所有独特的,25%重复和100%重复输入之间是一致的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.