Jak skutecznie iterować po każdym wpisie na mapie Java?


3301

Jeśli mam obiekt implementujący Mapinterfejs w Javie i chcę iterować każdą zawartą w nim parę, jaki jest najbardziej efektywny sposób przeglądania mapy?

Czy kolejność elementów będzie zależeć od konkretnej implementacji mapy, którą mam dla interfejsu?


38
W Javie 8 za pomocą wyrażenia Lambda: stackoverflow.com/a/25616206/1503859
Nitin Mahesh

Odpowiedzi:


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

93
Jeśli to zrobisz, nie zadziała, ponieważ Entry jest zagnieżdżoną klasą na mapie. java.sun.com/javase/6/docs/api/java/util/Map.html
ScArcher2

266
możesz napisać import jako „import java.util.Map.Entry;” i to zadziała.
jjujuma

55
@Pureferret Jedynym powodem, dla którego warto użyć iteratora, jest konieczność wywołania jego removemetody. W takim przypadku ta inna odpowiedź pokazuje, jak to zrobić. W przeciwnym razie udoskonalona pętla, jak pokazano w powyższej odpowiedzi, jest właściwym rozwiązaniem.
assylias

102
Uważam, że formularz Map.Entry jest bardziej przejrzysty niż import klasy wewnętrznej do bieżącej przestrzeni nazw.
JosiahYoder-deactive, z wyjątkiem ..

31
Pamiętaj, że możesz użyć map.values()lub map.keySet()jeśli chcesz przewijać tylko wartości lub klucze.
czwartek

1214

Podsumowując pozostałe odpowiedzi i łącząc je z tym, co wiem, znalazłem 10 głównych sposobów, aby to zrobić (patrz poniżej). Napisałem też kilka testów wydajności (patrz wyniki poniżej). Na przykład, jeśli chcemy znaleźć sumę wszystkich kluczy i wartości mapy, możemy napisać:

  1. Korzystanie z iteratora i mapy

    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. Korzystanie z foreach i Map.Entry

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

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
  4. Korzystanie z keySet i foreach

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
  5. Korzystanie z keySet i iteratora

    long i = 0;
    Iterator<Integer> itr2 = map.keySet().iterator();
    while (itr2.hasNext()) {
        Integer key = itr2.next();
        i += key + map.get(key);
    }
  6. Korzystanie z i 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. Korzystanie z interfejsu API Java 8 Stream

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
  8. Korzystanie z równoległego interfejsu API Java 8 Stream

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
  9. Korzystanie z IterableMap zApache Collections

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
  10. Korzystanie z MutableMap kolekcji Eclipse (CS)

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

Testy wydajności (tryb = średni czas, system = Windows 8.1 64-bit, Intel i7-4790 3,60 GHz, 16 GB)

  1. W przypadku małej mapy (100 elementów) najlepszy wynik to 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. W przypadku mapy z 10000 elementami najlepszy wynik to 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. W przypadku mapy z 100000 elementów najlepszy wynik to 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

Wykresy (testy wydajności w zależności od wielkości mapy)

Wpisz opis zdjęcia tutaj

Tabela (testy wydajności w zależności od wielkości mapy)

          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

Wszystkie testy są na GitHub .


9
@Viacheslav: bardzo ładna odpowiedź. Zastanawiam się tylko, w jaki sposób przeszkadza się api Java8, w twoim benchmarku, przechwytywanie lambdas ... (np. long sum = 0; map.forEach( /* accumulate in variable sum*/);Rejestruje sumdługi, który może być wolniejszy niż stream.mapToInt(/*whatever*/).sumna przykład powiedzmy . Oczywiście nie zawsze możesz uniknąć przechwytywania stanu, ale może to być uzasadniony dodatek do ławki
GPI

17
Twój 8 test jest nieprawidłowy. uzyskuje dostęp do tej samej zmiennej z różnych wątków bez synchronizacji. Zmień, aby AtomicIntegerrozwiązać problem.
talex 13.09.16

44
@ZhekaKozlov: spójrz na olbrzymie wartości błędów. Weź pod uwagę, że wynik testu x±eimplikuje, że wynik był w przedziale od x-edo x+e, więc najszybszy wynik ( 1184.767±332.968) waha się od 852do 1518, podczas gdy drugi najwolniejszy ( 1706.676±436.867) biegnie między 1270i 2144, więc wyniki wciąż znacznie się pokrywają. Spójrzmy teraz na najwolniejszym rezultacie 3289.866±1445.564, co implikuje rozbieżności pomiędzy 1844a 4735i wiem , że te wyniki badań są bez znaczenia.
Holger

8
Co powiesz na porównanie 3 głównych implementacji: HashMap, LinkedHashMap i TreeMap?
Thierry

9
# 1 i # 6 są dokładnie takie same. Używanie whilevs. forpętla nie jest inną techniką iteracji. I jestem zaskoczony, że mają takie różnice między nimi w waszych testach - co sugeruje, że testy nie są odpowiednio odizolowane od czynników zewnętrznych niezwiązanych z rzeczami, które zamierzacie testować.
ErikE

293

W Javie 8 możesz to zrobić czysto i szybko, korzystając z nowych funkcji lambdas:

 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));

Typ ki vzostanie wyprowadzony przez kompilator i nie trzeba go Map.Entryjuż używać .

Bułka z masłem!


12
W zależności od tego, co chcesz zrobić z mapą, możesz także użyć interfejsu API streamu dla wpisów zwróconych przez map.entrySet().stream() docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Vitalii Fedorenko

1
To nie zadziała, jeśli chcesz odwoływać się do nie-końcowych zmiennych zadeklarowanych poza wyrażeniem lambda z poziomu forEach () ...
Chris

7
@Chris Correct. To nie zadziała, jeśli spróbujesz użyć efektywnie zmiennych nie-końcowych spoza lambda.
Koordynator

242

Tak, kolejność zależy od konkretnej implementacji mapy.

@ ScArcher2 ma bardziej elegancką składnię Java 1.5 . W 1.4 zrobiłbym coś takiego:

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

41
Preferuj pętlę for while niż ... for (Iterator wpisy = myMap.entrySet (). Iterator (); wpisy.hasNext ();) {...} Dzięki tej składni zakres „wpisów” jest ograniczony do samej pętli for .
jai

8
@jpredham Masz rację, że użycie forkonstruktu as for (Entry e : myMap.entrySet)nie pozwoli ci zmodyfikować kolekcji, ale przykład, jak wspomniano @HanuAthena, powinien działać, ponieważ daje ci Iteratorzakres. (Chyba, że ​​coś mi
umknie

1
IntelliJ daje mi błędy Entry thisEntry = (Entry) entries.next();: nie rozpoznaje Entry. Czy to pseudokod dla czegoś innego?
JohnK,

1
@JohnK spróbuj zaimportować java.util.Map.Entry.
pkaeding

1
To rozwiązanie nie będzie działać, jeśli masz klucz liczby całkowitej i klucz String.

139

Typowy kod iteracji po mapie to:

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

HashMapjest implementacją mapy kanonicznej i nie daje gwarancji (lub chociaż nie powinna zmieniać kolejności, jeśli nie jest na niej wykonywana żadna operacja mutacji). SortedMapzwróci wpisy w oparciu o naturalną kolejność kluczy, lub a Comparator, jeśli podano. LinkedHashMapzwróci wpisy w kolejności wprowadzania lub kolejności dostępu, w zależności od tego, jak zostało skonstruowane. EnumMapzwraca wpisy w naturalnej kolejności kluczy.

(Aktualizacja: Myślę, że to już nie jest prawda. ) Uwaga: IdentityHashMap entrySetiterator ma obecnie osobliwą implementację, która zwraca tę samą Map.Entryinstancję dla każdego elementu w entrySet! Jednak za każdym razem, gdy nowy iterator przesuwa się, Map.Entryjest aktualizowany.


6
EnumMap ma również to szczególne zachowanie wraz z IdentityHashMap
Premraj

1
„LinkedHashMap albo zwróci wpisy w [...] kolejności dostępu [...]” ... więc masz dostęp do elementów w kolejności, w której je uzyskasz? Albo tautologiczna, albo coś interesującego, co mogłoby posłużyć się dygresją. ;-)
jpaugh

5
@jpaugh Tylko bezpośredni dostęp do LinkedHashMaphrabiego. Tych przez iterator, spliterator, entrySet, itd., Nie zmieniają kolejność.
Tom Hawtin - tackline

1
1. chociażjeśli ? 2. Ostatni akapit może skorzystać z odświeżenia.
Peter Mortensen

120

Przykład użycia iteratora i ogólnych:

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

14
Powinieneś umieścić Iteratorpętlę for, aby ograniczyć jej zasięg.
Steve Kuo,

@ SteveKuo Co rozumiesz przez „ograniczenie zakresu”?
StudioWorks

12
@StudioWorks for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }. Używając tego konstruktu ograniczamy zakres (widoczność zmiennej) entriesdo pętli for.
ComFreek

3
@ComFreek Oh, rozumiem. Nie wiedziałem, że to tak bardzo się liczy.
StudioWorks

101

To jest dwuczęściowe pytanie:

Jak iterować po wpisach mapy - @ ScArcher2 doskonale na to odpowiedział .

Jaka jest kolejność iteracji - jeśli tylko używasz Map, to mówiąc ściśle, nie ma gwarancji na zamówienie . Tak więc nie powinieneś polegać na kolejności podanej przez jakąkolwiek implementację. Jednak SortedMapinterfejs się rozszerza Mapi zapewnia dokładnie to, czego szukasz - implementacje wyeliminują spójny porządek sortowania.

NavigableMapjest kolejnym przydatnym rozszerzeniem - jest to SortedMapdodatkowe metody wyszukiwania wpisów według ich uporządkowanej pozycji w zestawie kluczy. Więc potencjalnie może usunąć potrzebę iteracji w pierwszej kolejności - może być w stanie znaleźć konkretnego entryjesteś po użyciu higherEntry, lowerEntry, ceilingEntrylub floorEntrymetod. Ta descendingMapmetoda daje nawet wyraźną metodę odwrócenia kolejności przechodzenia .


83

Istnieje kilka sposobów na iterację po mapie.

Oto porównanie ich wydajności dla wspólnego zestawu danych przechowywanego na mapie poprzez zapisanie miliona par klucz-wartość na mapie i będzie iterować po mapie.

1) Korzystanie entrySet()z każdej pętli

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

50 milisekund

2) Korzystanie keySet()z każdej pętli

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

76 milisekund

3) Używanie entrySet()i iterator

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

50 milisekund

4) Korzystanie keySet()i iterator

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

75 milisekund

Odniosłem się this link.


1
Czasy uruchamiania pochodzą z artykułu, który nie korzysta z uprzęży Java Microbenchmarking. Czasy są więc niewiarygodne, ponieważ na przykład kod mógł zostać całkowicie zoptymalizowany przez kompilator JIT.
AlexB

58

Prawidłowym sposobem na to jest użycie zaakceptowanej odpowiedzi, ponieważ jest ona najbardziej wydajna. Uważam, że następujący kod wygląda na nieco czystszy.

for (String key: map.keySet()) {
   System.out.println(key + "/" + map.get(key));
}

15
To nie jest najlepsze podejście, o wiele bardziej wydajne jest użycie entrySet (). Findbugs oflaguje ten kod (patrz findbugs.sourceforge.net/… )
Jeff Olson,

6
@JeffOlson meh, niezupełnie. wyszukiwanie mapy to O (1), więc obie pętle zachowują się w ten sam sposób. wprawdzie będzie to trochę wolniejsze w mikroprocesorze, ale czasem też to robię, bo nie znoszę pisać argumentów typu. Prawdopodobnie nigdy nie będzie to wąskim gardłem w wydajności, więc skorzystaj z niego, jeśli kod będzie bardziej czytelny.
kritzikratzi

4
bardziej szczegółowo: O(1) = 2*O(1)jest właściwie definicją dużej notacji O. masz rację, ponieważ działa nieco wolniej, ale pod względem złożoności są takie same.
kritzikratzi

2
przez kolizję czy nie, miałem na myśli, że nie ma znaczenia, czy masz kilka kolizji, oczywiście to inna historia, jeśli masz tylko kolizje. więc jesteś bardzo małostkowy, ale tak, to, co mówisz, jest prawdą.
kritzikratzi

2
@Jeff Olson: komentarze, że złożoność „Big O” nie zmienia się, gdy istnieje tylko stały czynnik, są poprawne. Jednak dla mnie ważne jest, czy operacja zajmuje godzinę, czy dwie godziny. Co ważniejsze, należy podkreślić, że czynnik ten nie jest 2, ponieważ iteracja nad entrySet()nie w ogóle zawiera przegląd; to tylko liniowe przejście wszystkich wpisów. W przeciwieństwie do tego, iteracja keySet()i wykonywanie wyszukiwania na klucz powoduje jedno wyszukiwanie na klucz, więc mówimy tutaj o zerowych przeglądach vs. n przeglądach tutaj, gdzie n jest rozmiarem pliku Map. Więc czynnik jest znacznie większy niż 2
Holger

56

Do Twojej wiadomości możesz także użyć, map.keySet()a map.values()jeśli interesują Cię tylko klucze / wartości mapy, a nie inne.


42

W Javie 8 możesz iterować Mapę, używając forEach i wyrażenia lambda,

map.forEach((k, v) -> System.out.println((k + ":" + v)));

40

Z Eclipse Kolekcje , należy użyć forEachKeyValuemetody w MapIterableinterfejsie, który jest dziedziczony przez MutableMapi ImmutableMapinterfejsów oraz ich implementacji.

MutableBag<String> result = Bags.mutable.empty();
MutableMap<Integer, String> map = Maps.mutable.of(1, "One", 2, "Two", 3, "Three");
map.forEachKeyValue((key, value) -> result.add(key + value));
Assert.assertEquals(Bags.mutable.of("1One", "2Two", "3Three"), result);

Korzystając z anonimowej klasy wewnętrznej, możesz napisać kod w następujący sposób:

final MutableBag<String> result = Bags.mutable.empty();
MutableMap<Integer, String> map = Maps.mutable.of(1, "One", 2, "Two", 3, "Three");
map.forEachKeyValue(new Procedure2<Integer, String>()
{
    public void value(Integer key, String value)
    {
        result.add(key + value);
    }
});
Assert.assertEquals(Bags.mutable.of("1One", "2Two", "3Three"), result);

Uwaga: jestem osobą odpowiedzialną za kolekcje Eclipse.


36

Lambda Expression Java 8

W Javie 1.8 (Java 8) stało się dużo łatwiejsze przy użyciu foreach metody z operacji kruszywa ( operacje Stream ), który wygląda podobnie do iteratorów z iterable Interface.

Wystarczy skopiować instrukcję wklej poniżej i zmień nazwę zmiennej HashMap z hm na zmienną HashMap, aby wydrukować parę klucz-wartość.

HashMap<Integer,Integer> hm = new HashMap<Integer, Integer>();
/*
 *     Logic to put the Key,Value pair in your HashMap hm
 */

// Print the key value pair in one line.

hm.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));

// Just copy and paste above line to your code.

Poniżej znajduje się przykładowy kod, który próbowałem przy użyciu wyrażenia Lambda . Te rzeczy są takie fajne. Musisz spróbować.

HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();
    Random rand = new Random(47);
    int i = 0;
    while(i < 5) {
        i++;
        int key = rand.nextInt(20);
        int value = rand.nextInt(50);
        System.out.println("Inserting key: " + key + " Value: " + value);
        Integer imap = hm.put(key, value);
        if( imap == null) {
            System.out.println("Inserted");
        } else {
            System.out.println("Replaced with " + imap);
        }               
    }

    hm.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));

Output:

Inserting key: 18 Value: 5
Inserted
Inserting key: 13 Value: 11
Inserted
Inserting key: 1 Value: 29
Inserted
Inserting key: 8 Value: 0
Inserted
Inserting key: 2 Value: 7
Inserted
key: 1 value:29
key: 18 value:5
key: 2 value:7
key: 8 value:0
key: 13 value:11

Można również użyć Spliteratora do tego samego.

Spliterator sit = hm.entrySet().spliterator();

AKTUALIZACJA


Łącznie z linkami do dokumentacji do Dokumentów Oracle. Aby uzyskać więcej informacji na temat Lambda, przejdź do tego linku i musisz przeczytać Aggregate Operations, a dla Spliteratora przejdź do tego linku .


35

Teoretycznie najskuteczniejszy sposób będzie zależeć od tego, która implementacja mapy. Oficjalnym sposobem na to jest wywołanie map.entrySet(), które zwraca zestaw Map.Entry, z których każdy zawiera klucz i wartość ( entry.getKey()i entry.getValue()).

W idiosynkratycznej implementacji może mieć znaczenie, czy używasz map.keySet(), map.entrySet()czy coś innego. Ale nie mogę wymyślić powodu, dla którego ktoś mógłby tak to napisać. Najprawdopodobniej nie ma to wpływu na wydajność tego, co robisz.

I tak, kolejność zależy od implementacji - a także (ewentualnie) kolejności wprowadzania i innych trudnych do kontrolowania czynników.

[edytuj] Napisałem valueSet()pierwotnie, ale oczywiście entrySet()jest to odpowiedź.


35

Java 8

Mamy forEachmetodę, która akceptuje wyrażenie lambda . Mamy również interfejsy API strumieni . Rozważ mapę:

Map<String,String> sample = new HashMap<>();
sample.put("A","Apple");
sample.put("B", "Ball");

Iterate over keys:

sample.keySet().forEach((k) -> System.out.println(k));

Iterate over values:

sample.values().forEach((v) -> System.out.println(v));

Iteruj po wpisach (Korzystanie z forEach i strumieni):

sample.forEach((k,v) -> System.out.println(k + ":" + v)); 
sample.entrySet().stream().forEach((entry) -> {
            Object currentKey = entry.getKey();
            Object currentValue = entry.getValue();
            System.out.println(currentKey + ":" + currentValue);
        });

Zaletą strumieni jest to, że można je łatwo zrównoleglać w razie potrzeby. Musimy po prostu użyć parallelStream()zamiast stream()powyższego.

forEachOrderedkontra forEachze strumieniami? forEachNie wynika kolejności napotkać (jeśli określona) i jest z natury nie deterministyczny w przyrodzie, gdzie jako forEachOrderedma. Tak forEachnie gwarantuje, że zamówienie zostanie utrzymane. Należy również sprawdzić to na dłużej.


32

Java 8:

Możesz użyć wyrażeń lambda:

myMap.entrySet().stream().forEach((entry) -> {
    Object currentKey = entry.getKey();
    Object currentValue = entry.getValue();
});

Aby uzyskać więcej informacji, wykonaj następujące czynności .


@injecteer: Wygląda na motyw wyrażeń lambda
humblerookie

9
Nie potrzebujesz strumienia, jeśli chcesz po prostu iterować po mapie. myMap.forEach( (currentKey,currentValue) -> /* action */ );jest o wiele bardziej zwięzłe.
Holger,

28

Wypróbuj to w Javie 1.4:

for( Iterator entries = myMap.entrySet().iterator(); entries.hasNext();){

  Entry entry = (Entry) entries.next();

  System.out.println(entry.getKey() + "/" + entry.getValue());

  //...
}

25

W Mapie można iterację ponad keysi / lub valuesi / lub both (e.g., entrySet) zależy od tego, czy jest zainteresowany:

  1. Iteruj po keys -> keySet()mapie:

    Map<String, Object> map = ...;
    
    for (String key : map.keySet()) {
        //your Business logic...
    }
  2. Iteruj po values -> values()mapie:

    for (Object value : map.values()) {
        //your Business logic...
    }
  3. Iteruj po both -> entrySet()mapie:

    for (Map.Entry<String, Object> entry : map.entrySet()) {
        String key = entry.getKey();
        Object value = entry.getValue();
        //your Business logic...
    }

_Ponadto istnieją 3 różne sposoby iteracji poprzez HashMap. Są jak poniżej__

//1.
for (Map.Entry entry : hm.entrySet()) {
    System.out.print("key,val: ");
    System.out.println(entry.getKey() + "," + entry.getValue());
}

//2.
Iterator iter = hm.keySet().iterator();
while(iter.hasNext()) {
    Integer key = (Integer)iter.next();
    String val = (String)hm.get(key);
    System.out.println("key,val: " + key + "," + val);
}

//3.
Iterator it = hm.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry entry = (Map.Entry) it.next();
    Integer key = (Integer)entry.getKey();
    String val = (String)entry.getValue();
    System.out.println("key,val: " + key + "," + val);
}

24

Najbardziej kompaktowy z Javą 8:

map.entrySet().forEach(System.out::println);

21

Jeśli masz ogólną mapę bez typu, możesz użyć:

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

20
public class abcd{
    public static void main(String[] args)
    {
       Map<Integer, String> testMap = new HashMap<Integer, String>();
        testMap.put(10, "a");
        testMap.put(20, "b");
        testMap.put(30, "c");
        testMap.put(40, "d");
        for (Integer key:testMap.keySet()) {
            String value=testMap.get(key);
            System.out.println(value);
        }
    }
}

LUB

public class abcd {
    public static void main(String[] args)
    {
       Map<Integer, String> testMap = new HashMap<Integer, String>();
        testMap.put(10, "a");
        testMap.put(20, "b");
        testMap.put(30, "c");
        testMap.put(40, "d");
        for (Entry<Integer, String> entry : testMap.entrySet()) {
            Integer key=entry.getKey();
            String value=entry.getValue();
        }
    }
}

17

Jeśli mam obiekt implementujący interfejs Map w Javie i chcę iterować każdą zawartą w nim parę, jaki jest najbardziej efektywny sposób przeglądania mapy?

Jeśli wydajność zapętlania kluczy jest priorytetem dla Twojej aplikacji, wybierz Mapimplementację, która utrzymuje klucze w żądanej kolejności.

Czy kolejność elementów będzie zależeć od konkretnej implementacji mapy, którą mam dla interfejsu?

Tak, absolutnie.

  • Niektóre Mapimplementacje obiecują określony porządek iteracji, inne nie.
  • Różne implementacje Maputrzymują różne kolejność par klucz-wartość.

Zobacz tabelę, którą utworzyłem, podsumowującą różne Mapimplementacje zawarte w Javie 11. W szczególności zwróć uwagę na kolumnę kolejności iteracji . Kliknij / dotknij, aby powiększyć.

Tabela implementacji map w Javie 11, porównanie ich funkcji

Widać, że są cztery Mapimplementacje utrzymujące zamówienie :

  • TreeMap
  • ConcurrentSkipListMap
  • LinkedHashMap
  • EnumMap

NavigableMap berło

Dwa z nich implementują NavigableMapinterfejs: TreeMapiConcurrentSkipListMap .

Starszy SortedMapinterfejs jest skutecznie zastępowany przez nowszyNavigableMap interfejs. Ale możesz znaleźć implementacje innych firm implementujące tylko starszy interfejs.

Naturalny porządek

Jeśli chcesz, Mapaby pary układały się według „naturalnego porządku” klucza, użyj TreeMaplub ConcurrentSkipListMap. Termin „porządek naturalny” oznacza klasę kluczy Comparable. Wartość zwrócona przezcompareTo metodę służy do porównania podczas sortowania.

Zamówienie klienta

Jeśli chcesz określić niestandardową procedurę sortowania kluczy, która będzie używana w celu utrzymania uporządkowanej kolejności, przekaż Comparatorimplementację odpowiednią dla klasy kluczy. Użyj albo, TreeMapalbo ConcurrentSkipListMapprzekazując swoją Comparator.

Oryginalne zamówienie reklamowe

Jeśli chcesz, aby pary mapy były przechowywane w oryginalnej kolejności, w jakiej wstawiłeś je na mapie, użyj LinkedHashMap.

Kolejność wyliczania

Jeśli używasz wyliczenia, takiego jak DayOfWeeklub Monthjako klucze, użyj EnumMapklasy. Ta klasa jest nie tylko wysoce zoptymalizowana, aby zużywała bardzo mało pamięci i działała bardzo szybko, ale także utrzymuje pary w kolejności określonej przez wyliczenie. Na DayOfWeekprzykład klucz DayOfWeek.MONDAYzostanie najpierw znaleziony podczas iteracji, a kluczDayOfWeek.SUNDAY będzie ostatni.

Inne uwagi

Wybierając Mapimplementację, weź również pod uwagę:

  • NULLs. Niektóre implementacje zabraniają / akceptują wartość NULL jako klucz i / lub wartość.
  • Konkurencja. Jeśli manipulujesz mapą między wątkami, musisz użyć implementacji obsługującej współbieżność. Lub zawiń mapę Collections::synchronizedMap(mniej preferowane).

Oba te rozważania zostały przedstawione w powyższej tabeli graficznej.


Późny komentarz do odpowiedzi, która jest również spóźniona na przyjęcie (ale bardzo pouczająca). +1 ode mnie za wzmiankę EnumMap, ponieważ po raz pierwszy o tym słyszałem. Prawdopodobnie jest wiele przypadków, w których może się to przydać.
user991710

15

Kolejność zawsze będzie zależeć od konkretnej implementacji mapy. Korzystając z Java 8, możesz użyć jednego z tych:

map.forEach((k,v) -> { System.out.println(k + ":" + v); });

Lub:

map.entrySet().forEach((e) -> {
            System.out.println(e.getKey() + " : " + e.getValue());
        });

Wynik będzie taki sam (ta sama kolejność). EntrySet poparty mapą, dzięki czemu otrzymujesz to samo zamówienie. Drugi jest przydatny, ponieważ pozwala używać lambd, np. Jeśli chcesz drukować tylko obiekty całkowite, które są większe niż 5:

map.entrySet()
    .stream()
    .filter(e-> e.getValue() > 5)
    .forEach(System.out::println);

Poniższy kod pokazuje iterację poprzez LinkedHashMap i normalną HashMap (przykład). Zobaczysz różnicę w kolejności:

public class HMIteration {


    public static void main(String[] args) {
        Map<Object, Object> linkedHashMap = new LinkedHashMap<>();
        Map<Object, Object> hashMap = new HashMap<>();

        for (int i=10; i>=0; i--) {
            linkedHashMap.put(i, i);
            hashMap.put(i, i);
        }

        System.out.println("LinkedHashMap (1): ");
        linkedHashMap.forEach((k,v) -> { System.out.print(k + " (#="+k.hashCode() + "):" + v + ", "); });

        System.out.println("\nLinkedHashMap (2): ");

        linkedHashMap.entrySet().forEach((e) -> {
            System.out.print(e.getKey() + " : " + e.getValue() + ", ");
        });


        System.out.println("\n\nHashMap (1): ");
        hashMap.forEach((k,v) -> { System.out.print(k + " (#:"+k.hashCode() + "):" + v + ", "); });

        System.out.println("\nHashMap (2): ");

        hashMap.entrySet().forEach((e) -> {
            System.out.print(e.getKey() + " : " + e.getValue() + ", ");
        });
    }
}

LinkedHashMap (1):

10 (# = 10): 10, 9 (# = 9): 9, 8 (# = 8): 8, 7 (# = 7): 7, 6 (# = 6): 6, 5 (# = 5 ): 5, 4 (# = 4): 4, 3 (# = 3): 3, 2 (# = 2): 2, 1 (# = 1): 1, 0 (# = 0): 0,

LinkedHashMap (2):

10: 10, 9: 9, 8: 8, 7: 7, 6: 6, 5: 5, 4: 4, 3: 3, 2: 2, 1: 1, 0: 0,

HashMap (1):

0 (#: 0): 0, 1 (#: 1): 1, 2 (#: 2): 2, 3 (#: 3): 3, 4 (#: 4): 4, 5 (#: 5 ): 5, 6 (#: 6): 6, 7 (#: 7): 7, 8 (#: 8): 8, 9 (#: 9): 9, 10 (#: 10): 10,

HashMap (2):

0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10,


14
    Iterator iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry element = (Map.Entry)it.next();
        LOGGER.debug("Key: " + element.getKey());
        LOGGER.debug("value: " + element.getValue());    
    }

14

Użyj Java 8:

map.entrySet().forEach(entry -> System.out.println(entry.getValue()));

13

Możesz to zrobić przy użyciu ogólnych:

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
    Map.Entry<Integer, Integer> entry = entries.next();
    System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}

12

Skutecznym rozwiązaniem iteracyjnym nad mapą jest pętla „dla każdej” od Java 5 do Java 7. Oto ona:

for (String key : phnMap.keySet()) {
    System.out.println("Key: " + key + " Value: " + phnMap.get(key));
}

W Javie 8 możesz używać wyrażenia lambda do iteracji po mapie. To ulepszone „forEach”

phnMap.forEach((k,v) -> System.out.println("Key: " + k + " Value: " + v));

Jeśli chcesz napisać warunek dla lambda, możesz napisać w następujący sposób:

phnMap.forEach((k,v)->{
    System.out.println("Key: " + k + " Value: " + v);
    if("abc".equals(k)){
        System.out.println("Hello abc");
    }
});

10
           //Functional Oprations
            Map<String, String> mapString = new HashMap<>();
            mapString.entrySet().stream().map((entry) -> {
                String mapKey = entry.getKey();
                return entry;
            }).forEach((entry) -> {
                String mapValue = entry.getValue();
            });

            //Intrator
            Map<String, String> mapString = new HashMap<>();
            for (Iterator<Map.Entry<String, String>> it = mapString.entrySet().iterator(); it.hasNext();) {
                Map.Entry<String, String> entry = it.next();
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();
            }

            //Simple for loop
            Map<String, String> mapString = new HashMap<>();
            for (Map.Entry<String, String> entry : mapString.entrySet()) {
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();

            }

10

Można to zrobić na wiele sposobów. Poniżej znajduje się kilka prostych kroków:

Załóżmy, że masz jedną mapę, np .:

Map<String, Integer> m = new HashMap<String, Integer>();

Następnie możesz zrobić coś takiego jak poniżej, aby iterować elementy mapy.

// ********** Using an iterator ****************
Iterator<Entry<String, Integer>> me = m.entrySet().iterator();
while(me.hasNext()){
    Entry<String, Integer> pair = me.next();
    System.out.println(pair.getKey() + ":" + pair.getValue());
}

// *********** Using foreach ************************
for(Entry<String, Integer> me : m.entrySet()){
    System.out.println(me.getKey() + " : " + me.getValue());
}

// *********** Using keySet *****************************
for(String s : m.keySet()){
    System.out.println(s + " : " + m.get(s));
}

// *********** Using keySet and iterator *****************
Iterator<String> me = m.keySet().iterator();
while(me.hasNext()){
    String key = me.next();
    System.out.println(key + " : " + m.get(key));
}

9
package com.test;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("ram", "ayodhya");
        map.put("krishan", "mathura");
        map.put("shiv", "kailash");

        System.out.println("********* Keys *********");
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println(key);
        }

        System.out.println("********* Values *********");
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }

        System.out.println("***** Keys and Values (Using for each loop) *****");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + "\t Value: "
                    + entry.getValue());
        }

        System.out.println("***** Keys and Values (Using while loop) *****");
        Iterator<Entry<String, String>> entries = map.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry<String, String>) entries
                    .next();
            System.out.println("Key: " + entry.getKey() + "\t Value: "
                    + entry.getValue());
        }

        System.out
                .println("** Keys and Values (Using java 8 using lambdas )***");
        map.forEach((k, v) -> System.out
                .println("Key: " + k + "\t value: " + v));
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.