我将数据存储在HashMap中,我想通过多个线程同时访问,以分割对项目所做的工作。
通常(以List为例)我只是给每个线程一个索引来开始,并且可以很容易地分割这样的工作:
for(int i = startIndex; i < startIndex+batchSize && i < list.size(); i++)
{
Item a = list.get(i);
// do stuff with the Item
}
当然这不适用于HashMap,因为我无法通过索引访问它。
是否有一种简单的方法只迭代地图的一部分?我是否应该为这种情况使用另一种数据结构?
我读到了有关SortedMap的内容,但它有太多我不需要的开销(对项目进行排序)。我有很多数据和性能至关重要。
任何提示都将受到高度赞赏。
答案 0 :(得分:3)
首先,您不应该使用HashMap,因为迭代顺序是未定义的。使用LinkedHashMap
,其迭代顺序与插入顺序相同(至少已定义),或使用TreeMap
,其迭代顺序是自然排序顺序。我建议使用LinkedHashMap,因为插入一个条目会使地图切片变得不可预测。
要分割地图,请使用以下代码:
LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
for (Map.Entry<Integer, String> entry : new ArrayList<Map.Entry<Integer,String>>(map.entrySet()).subList(start, end)) {
Integer key = entry.getKey();
String value = entry.getValue();
// Do something with the entry
}
我已经列出了代码,但扩展了它相当于:
List<Map.Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer,String>>();
entryList.addAll(map.entrySet());
entryList = entryList.subList(start, end); // You provide the start and end index
for (Map.Entry<Integer, String> entry : entryList) ...
答案 1 :(得分:1)
如果您只进行了几次遍历,或者如果地图没有更改,您可以获得一组密钥,然后将其发送到数组。从那里它几乎是你的正常方法。但很明显,如果HashMap发生了变化,那么你将不得不再次进行这两项操作,这可能会非常昂贵。
答案 2 :(得分:1)
使用HashMap#keySet - &gt;设置#toArray你会得到一组键。
使用此数组,您可以像以前一样进行操作,保留键数组并将它们传递给您的线程。然后每个线程只访问它已分配的键,最后你只能使用那些键访问HashMap的给定分区的条目。
答案 3 :(得分:0)
除非您的地图是巨大的,否则与在您打算完成的工作相比,在另一个线程上启动任务的成本相比,迭代地图的成本很小。
出于这个原因,分割工作的最简单方法可能是将Map转换为数组并将其分解。
final Map<K, V> map =
final ExecutorServices es =
final int portions = Runtime.getRuntime().availableProcessors();
final Map.Entry<K,V>[] entries = (Map.Entry<K,V>[]) map.entrySet().toArray(new Map.Entry[map.size()]);
final int portionSize = (map.size() + portions-1)/ portions;
for(int i = 0; i < portions; i++) {
final int start = i * portionSize;
final int end = Math.min(map.size(), (i + 1) * portionSize);
es.submit(new Runnable() {
public void run() {
for(int j=start; j<end;j++) {
Map.Entry<K,V> entry = entries[j];
// process entry.
}
}
});
}