2011-07-10 9 views
2

複数のスレッドを同時にアクセスしたいHashMapにデータが格納されているため、アイテムに対して行われた作業が分割されます。マップの一部のみを繰り返します。

(例えばリスト付き)通常、私はちょうど、各スレッドにして開始するための指標を与えるだろうと簡単にこのような作業を分割することができます:私ので、もちろん

for(int i = startIndex; i < startIndex+batchSize && i < list.size(); i++) 
{ 
    Item a = list.get(i); 
    // do stuff with the Item 
} 

のHashMapでこのdoesntの仕事インデックスを介してアクセスすることはできません。

地図の一部だけを簡単に反復する方法はありますか?このケースでは別のデータ構造を使用するべきですか?

私はSortedMapについて読んでいますが、あまりにもオーバーヘッドがあります(項目をソートする必要はありません)。私は多くのデータを持っており、パフォーマンスが重要です。

どのようなヒントも高く評価されます。

+0

地図の分割方法を教えてください。 – skaffman

+0

よくわからない質問があります。 :)私はマップが私が持っているスレッドの数(例えば8)の多くの部分に分割したいと思います。可能であれば、パーティショニングはコストのかかる操作であってはなりません。 – magnattic

+0

*多くのデータ* ... –

答えて

1

トラバーサルを数回行うか、マップが変更されない場合は、キーセットを取得して配列に送信できます。そこから、あなたの通常の方法がかなりあります。しかし、明らかにHashMapが変更された場合、これらの2つの操作をやり直す必要があり、コストが高くなる可能性があります。

+0

幸いにも、HashMapはスレッドによって変更されません。 toArray()メソッドが安いと仮定して、あなたのメソッドはうまく聞こえます。それを試してみて、パフォーマンスがどれくらい良いかを見て回ってください。 – magnattic

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 
    } 

私はライニングコード持っているが、それは同等ですアウト拡大:HashMapの#のkeySetで

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) ... 
+0

TreeMapはオプションではありません。なぜなら、アイテムの順序はパフォーマンス・キラーなので、アイテムの特別な順序は必要ありません。私が作業している間にMapが変更されない場合、LinkedHashMapを使用する必要がありますか?私はアイテムの順序を気にしないので、なぜそれが定義されていることが重要ですか? – magnattic

+0

誰でも、entryListのソリューションをありがとうございます。それをRoss Larsonのアイデアと比較し、より速く実行するものを見ます。 :) – magnattic

+0

1つのスレッドでアイテム1から5を、別のスレッドでアイテム6からアイテム10を要求すると、両方で同じアイテムが得られる可能性があります - ハッシュマップの反復順序は定義されていません試してみることができます) – Bohemian

1

- > #toArrayを設定すると、キーの配列が取得されます。

この配列では、前と同じようにキー配列を保持してスレッドに渡すことができます。その後、各スレッドは割り当てられたキーのみにアクセスし、最後にそれらのキーだけでHashMapの特定のパーティションのエントリにアクセスできます。

+0

+1 entrySet()。toArray() - 良いアイデア!私はそれを考えなかった! – Bohemian

+0

ありがとう!この問題について考える前に私は知らなかった:それはSOのすばらしいことだ - あなたは問題について考えるときに多くのことを学ぶ。私は "うーん、SetにtoArrayがあったらどうなるの?" - JavaDocをチェックしました - それは:) – emboss

0

マップが巨大でない限り、マップを反復するコストは、別のスレッドでタスクを開始するコストと比較して小さく、意図する作業に比べて些細なものです。

このため、作業を分割する最も簡単な方法は、マップを配列に変換して分割することです。

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. 
      } 
     } 
    }); 
} 
関連する問題