使用parallelstream()在Java 8

时间:2016-10-25 10:55:16

标签: java 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中丢失?

2 个答案:

答案 0 :(得分:16)

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方法和Map<Integer, Integer> map = list.parallelStream().collect(Collectors.toMap(i -> i, i -> i)); 方法以及内置mutable reduction:< / p>

ConcurrentMap

在上面的示例代码中使用该行代码,您可以放心,映射大小始终为10000. Stream API确保toMap收集到非线程安全容器中,即使在平行。这也意味着您不需要使用it is safe来保证安全,如果您特别想要Map作为结果而不是通用collect,则需要此收集器;但就线程安全性而言, public class OutputTerminal { public string type { get; set; } public string id { get; set; } public string connectedId { get; set; } public string terminalType { get; set; } public string connectedType { get; set; } } public class Position { public string type { get; set; } public string x { get; set; } public string y { get; set; } } public class Item { public string type { get; set; } public string id { get; set; } public string name { get; set; } public string memberCount { get; set; } public IList<OutputTerminal> outputTerminals { get; set; } public Position position { get; set; } public string isFinished { get; set; } public string isRecurring { get; set; } public string segmentId { get; set; } public string waitFor { get; set; } public string testId { get; set; } } public class Root { public string type { get; set; } public string currentStatus { get; set; } public string id { get; set; } public IList<Item> items { get; set; } } 可以使用两者。

答案 1 :(得分:3)

HashMap不是线程安全的,但ConcurrentHashMap是;改用

Map<String, String> map = new ConcurrentHashMap<>();

您的代码将按预期工作。

forEach()toMap()

的效果比较

在JVM预热后,使用1M元素,使用并行流并使用中间时间,forEach()版本始终比toMap()版本快2-3倍。

所有独特,25%重复和100%重复输入之间的结果一致。

相关问题