[java] Java Map의 각 항목을 효율적으로 반복하려면 어떻게해야합니까?

MapJava로 인터페이스를 구현하는 객체가 있고 그 안에 포함 된 모든 쌍을 반복하려면 맵을 통과하는 가장 효율적인 방법은 무엇입니까?

요소의 순서는 인터페이스에 대한 특정 맵 구현에 따라 달라 집니까?



답변

Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}


답변

다른 답변을 요약하고 내가 아는 것과 결합하기 위해 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. 사용 의 foreach의 Map.Entry를

    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
  3. Java 8에서 forEach 사용

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
  4. 사용 keySet 반환foreach 문을

    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. 사용 을 위해의 Map.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 스트림 API 사용

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
  8. Java 8 Stream API 병렬 사용

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
  9. 사용 IterableMap 의를Apache Collections

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
  10. 이클립스 (CS) 컬렉션의 MutableMap 사용

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

성능 테스트 (모드 = AverageTime, 시스템 = Windows 8.1 64 비트, Intel i7-4790 3.60GHz, 16GB)

  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

그래프 (지도 크기에 따른 성능 테스트)

여기에 이미지 설명을 입력하십시오

표 (지도 크기에 따른 성능 테스트)

          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에서 이루어 집니다.


답변

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더 이상은.

쉬워요!


답변

예, 순서는 특정지도 구현에 따라 다릅니다.

@ ScArcher2는보다 우아한 Java 1.5 구문을 가지고 있습니다. 1.4에서는 다음과 같은 작업을 수행합니다.

Iterator entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Entry thisEntry = (Entry) entries.next();
  Object key = thisEntry.getKey();
  Object value = thisEntry.getValue();
  // ...
}


답변

지도를 반복하는 일반적인 코드는 다음과 같습니다.

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반복자는 현재 같은 반환하는 특유의 구현이 Map.Entry의 모든 항목에 대한 인스턴스를 entrySet! 그러나 새로운 반복자가 진행할 때마다 Map.Entry가 업데이트됩니다.


답변

반복자와 제네릭을 사용하는 예 :

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();
  // ...
}


답변

이것은 두 가지 질문입니다.

@ ScArcher2 맵의 항목을 반복하는 방법 은 완벽하게 답변 했습니다.

반복 순서는 무엇인가 -만약 당신이 단지 사용 Map하고 엄격히 말하면, 순서 보장없다 . 따라서 구현에서 제공 한 순서에 의존해서는 안됩니다. 그러나 SortedMap인터페이스가 확장 Map되어 원하는 것을 정확하게 제공합니다. 구현은 일관된 정렬 순서를 제공합니다.

NavigableMap또 다른 유용한 확장입니다 -이것은 SortedMap키 세트에서 순서대로 위치를 찾는 추가 방법입니다. 그래서 가능성이 처음부터 반복의 필요성을 제거 할 수 있습니다 – 당신은 특정 찾을 수 있습니다 entry당신이 사용 후입니다 higherEntry, lowerEntry, ceilingEntry, 또는 floorEntry방법. 이 descendingMap방법 은 순회 순서취소 하는 명시적인 방법도 제공합니다 .