仅迭代Map的一部分

时间:2011-07-10 22:13:46

标签: java data-structures indexing hashmap iteration

我将数据存储在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的内容,但它有太多我不需要的开销(对项目进行排序)。我有很多数据和性能至关重要。

任何提示都将受到高度赞赏。

4 个答案:

答案 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.
            }
        }
    });
}