Javaマップの各エントリを効率的に反復するにはどうすればよいですか?

2008年09月06日に質問されました。  ·  閲覧回数 2.6M回  ·  ソース

iMack picture
2008年09月06日

JavaでMapインターフェースを実装するオブジェクトがあり、その中に含まれるすべてのペアを反復処理したい場合、マップを通過する最も効率的な方法は何ですか?

要素の順序は、インターフェイス用に持っている特定のマップ実装に依存しますか?

回答

ScArcher2 picture
2008年09月06日
5214
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}
Slava Vedenin picture
2016年02月23日
1304

他の答えを要約し、私が知っていることと組み合わせるために、私はこれを行うための10の主な方法を見つけました(以下を参照)。 また、いくつかのパフォーマンステストを作成しました(以下の結果を参照)。 たとえば、マップのすべてのキーと値の合計を求めたい場合は、次のように記述できます。

  1. イテレータMap.Entryの使用

    long i = 0;
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Integer> pair = it.next();
        i += pair.getKey() + pair.getValue();
    }
    
  2. foreachMap.Entryの使用

    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
    
  3. Java8から

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
    
  4. keySetforeachの使用

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
    
  5. keySetイテレータの使用

    long i = 0;
    Iterator<Integer> itr2 = map.keySet().iterator();
    while (itr2.hasNext()) {
        Integer key = itr2.next();
        i += key + map.get(key);
    }
    
  6. forMap.Entryの使用

    long i = 0;
    for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
        Map.Entry<Integer, Integer> entry = entries.next();
        i += entry.getKey() + entry.getValue();
    }
    
  7. Java 8 StreamAPIの使用

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
    
  8. Java 8 StreamAPIを並列に使用する

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
    
  9. Apache Collections IterableMapを使用する

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
    
  10. Eclipse(CS)コレクションのMutableMapの使用

    final long[] i = {0};
    mutableMap.forEachKeyValue((key, value) -> {
        i[0] += key + value;
    });
    

パフォーマンステスト(モード= AverageTime、システム= Windows 8.1 64ビット、Intel i7-4790 3.60 GHz、16 GB)

  1. 小さなマップ(100要素)の場合、スコア0.308が最適です

    Benchmark                          Mode  Cnt  Score    Error  Units
    test3_UsingForEachAndJava8         avgt  10   0.308 ±  0.021  µs/op
    test10_UsingEclipseMap             avgt  10   0.309 ±  0.009  µs/op
    test1_UsingWhileAndMapEntry        avgt  10   0.380 ±  0.014  µs/op
    test6_UsingForAndIterator          avgt  10   0.387 ±  0.016  µs/op
    test2_UsingForEachAndMapEntry      avgt  10   0.391 ±  0.023  µs/op
    test7_UsingJava8StreamApi          avgt  10   0.510 ±  0.014  µs/op
    test9_UsingApacheIterableMap       avgt  10   0.524 ±  0.008  µs/op
    test4_UsingKeySetAndForEach        avgt  10   0.816 ±  0.026  µs/op
    test5_UsingKeySetAndIterator       avgt  10   0.863 ±  0.025  µs/op
    test8_UsingJava8StreamApiParallel  avgt  10   5.552 ±  0.185  µs/op
    
  2. 要素が10000のマップの場合、スコア37.606が最適です。

    Benchmark                           Mode   Cnt  Score      Error   Units
    test10_UsingEclipseMap              avgt   10    37.606 ±   0.790  µs/op
    test3_UsingForEachAndJava8          avgt   10    50.368 ±   0.887  µs/op
    test6_UsingForAndIterator           avgt   10    50.332 ±   0.507  µs/op
    test2_UsingForEachAndMapEntry       avgt   10    51.406 ±   1.032  µs/op
    test1_UsingWhileAndMapEntry         avgt   10    52.538 ±   2.431  µs/op
    test7_UsingJava8StreamApi           avgt   10    54.464 ±   0.712  µs/op
    test4_UsingKeySetAndForEach         avgt   10    79.016 ±  25.345  µs/op
    test5_UsingKeySetAndIterator        avgt   10    91.105 ±  10.220  µs/op
    test8_UsingJava8StreamApiParallel   avgt   10   112.511 ±   0.365  µs/op
    test9_UsingApacheIterableMap        avgt   10   125.714 ±   1.935  µs/op
    
  3. 100000要素のマップの場合、スコア1184.767が最適です

    Benchmark                          Mode   Cnt  Score        Error    Units
    test1_UsingWhileAndMapEntry        avgt   10   1184.767 ±   332.968  µs/op
    test10_UsingEclipseMap             avgt   10   1191.735 ±   304.273  µs/op
    test2_UsingForEachAndMapEntry      avgt   10   1205.815 ±   366.043  µs/op
    test6_UsingForAndIterator          avgt   10   1206.873 ±   367.272  µs/op
    test8_UsingJava8StreamApiParallel  avgt   10   1485.895 ±   233.143  µs/op
    test5_UsingKeySetAndIterator       avgt   10   1540.281 ±   357.497  µs/op
    test4_UsingKeySetAndForEach        avgt   10   1593.342 ±   294.417  µs/op
    test3_UsingForEachAndJava8         avgt   10   1666.296 ±   126.443  µs/op
    test7_UsingJava8StreamApi          avgt   10   1706.676 ±   436.867  µs/op
    test9_UsingApacheIterableMap       avgt   10   3289.866 ±  1445.564  µs/op
    

グラフ(マップサイズに応じたパフォーマンステスト)

Enter image description here

テーブル(マップサイズに応じたパフォーマンステスト)

          100     600      1100     1600     2100
test10    0.333    1.631    2.752    5.937    8.024
test3     0.309    1.971    4.147    8.147   10.473
test6     0.372    2.190    4.470    8.322   10.531
test1     0.405    2.237    4.616    8.645   10.707
test2     0.376    2.267    4.809    8.403   10.910
test7     0.473    2.448    5.668    9.790   12.125
test9     0.565    2.830    5.952   13.220   16.965
test4     0.808    5.012    8.813   13.939   17.407
test5     0.810    5.104    8.533   14.064   17.422
test8     5.173   12.499   17.351   24.671   30.403

すべてのテストはGitHubで行われ

The Coordinator picture
2013年10月21日
305

Java 8では、新しいラムダ機能を使用して、クリーンで高速に実行できます。

 Map<String,String> map = new HashMap<>();
 map.put("SomeKey", "SomeValue");
 map.forEach( (k,v) -> [do something with key and value] );

 // such as
 map.forEach( (k,v) -> System.out.println("Key: " + k + ": Value: " + v));

kvのタイプはコンパイラーによって推測され、 Map.Entryを使用する必要はありません。

簡単-簡単!

pkaeding picture
2008年09月06日
251

はい、順序は特定のマップの実装によって異なります。

@ ScArcher2には、より洗練されたJava1.5構文があります。 1.4では、次のようにします。

Iterator entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Entry thisEntry = (Entry) entries.next();
  Object key = thisEntry.getKey();
  Object value = thisEntry.getValue();
  // ...
}
Tom Hawtin - tackline picture
2008年09月06日
141

マップを反復処理するための一般的なコードは次のとおりです。

Map<String,Thing> map = ...;
for (Map.Entry<String,Thing> entry : map.entrySet()) {
    String key = entry.getKey();
    Thing thing = entry.getValue();
    ...
}

HashMapは標準的なマップの実装であり、保証はしません(または、変更操作が実行されない場合は順序を変更しないでください)。 SortedMapは、キーの自然順序に基づいてエントリを返します。提供されている場合は、 Comparatorを返します。 LinkedHashMapは、構築方法に応じて、挿入順またはアクセス順のいずれかのエントリを返します。 EnumMapは、キーの自然な順序でエントリを返します。

(更新:これはもはや真実ではないと思います。 )注: IdentityHashMap entrySetイテレータには現在、 entrySet内のすべてのアイテムに対して同じMap.Entryインスタンスを返す固有の実装があります。 entrySet ! ただし、新しいイテレータが進むたびに、 Map.Entryが更新されます。

serg picture
2009年08月19日
126

イテレータとジェネリックの使用例:

Iterator<Map.Entry<String, String>> entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Map.Entry<String, String> entry = entries.next();
  String key = entry.getKey();
  String value = entry.getValue();
  // ...
}
serg10 picture
2008年09月06日
104

これは2つの部分からなる質問です。

マップのエントリを反復処理する方法[email protected] ScArcher2はそれに完全に答えています。

反復の順序は何ですか- Mapを使用しているだけの場合、厳密に言えば、順序の保証はありSortedMapインターフェイスはMapを拡張し、探しているものを正確に提供します。実装により、一貫した並べ替え順序が提供されます。

NavigableMapは、もう1つの便利な拡張機能です。これはSortedMapで、キーセット内の順序付けられた位置でエントリを検索するための追加のメソッドがあります。 したがって、これにより、最初から反復する必要がなくなる可能性があります。 higherEntrylowerEntryceilingEntryを使用した後、特定のentryを見つけることができる場合があります。 ceilingEntry 、またはfloorEntryメソッド。 descendingMapメソッドは、トラバーサル順序

Darshan Patel picture
2014年02月05日
86

マップ上で反復する方法はいくつかあります。

これは、マップに100万のキーと値のペアを格納することにより、マップに格納された一般的なデータセットのパフォーマンスを比較したもので、マップを反復処理します。

1)ループごとにentrySet()を使用する

for (Map.Entry<String,Integer> entry : testMap.entrySet()) {
    entry.getKey();
    entry.getValue();
}

50ミリ秒

2)ループごとにkeySet()を使用する

for (String key : testMap.keySet()) {
    testMap.get(key);
}

76ミリ秒

3) entrySet()とイテレータを使用する

Iterator<Map.Entry<String,Integer>> itr1 = testMap.entrySet().iterator();
while(itr1.hasNext()) {
    Map.Entry<String,Integer> entry = itr1.next();
    entry.getKey();
    entry.getValue();
}

50ミリ秒

4) keySet()とイテレータを使用する

Iterator itr2 = testMap.keySet().iterator();
while(itr2.hasNext()) {
    String key = itr2.next();
    testMap.get(key);
}

75ミリ秒

this link

Chris Dail picture
2008年09月08日
60

これを行う正しい方法は、最も効率的であるため、受け入れられた回答を使用することです。 次のコードは少しすっきりしているように見えます。

for (String key: map.keySet()) {
   System.out.println(key + "/" + map.get(key));
}
ckpwong picture
2008年09月06日
59

参考までに、マップのキー/値のみに関心があり、他には関心がない場合は、 map.keySet()map.values()使用することもできます。