我有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中丢失?
答案 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%重复输入之间的结果一致。