Wie iteriere ich effizient über jeden Eintrag in einer Java Map?

3302

Wenn ich ein Objekt habe, das die MapSchnittstelle in Java implementiert, und jedes darin enthaltene Paar durchlaufen möchte, wie gehe ich dann am effizientesten durch die Karte?

Hängt die Reihenfolge der Elemente von der spezifischen Kartenimplementierung ab, die ich für die Schnittstelle habe?

iMack
quelle
38
In Java 8 mit Lambda Expression: stackoverflow.com/a/25616206/1503859
Nitin Mahesh
5
Java 8: stackoverflow.com/questions/46898/…
akhil_mittal

Antworten:

5030
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}
ScArcher2
quelle
93
Wenn Sie dies tun, funktioniert dies nicht, da der Eintrag eine verschachtelte Klasse in Map ist. java.sun.com/javase/6/docs/api/java/util/Map.html
ScArcher2
266
Sie können den Import als "import java.util.Map.Entry;" schreiben. und es wird funktionieren.
Jjujuma
55
@Pureferret Der einzige Grund, warum Sie einen Iterator verwenden möchten, ist, wenn Sie seine removeMethode aufrufen müssen . Wenn dies der Fall ist, zeigt Ihnen diese andere Antwort , wie es geht. Andernfalls ist die erweiterte Schleife, wie in der obigen Antwort gezeigt, der richtige Weg.
Assylias
102
Ich glaube, das Formular Map.Entry ist klarer als das Importieren der inneren Klasse in den aktuellen Namespace.
Josiah Yoder
31
Beachten Sie, dass Sie nur Werte oder Schlüssel verwenden können map.values()oder verwenden map.keySet()möchten.
dguay
1215

Um die anderen Antworten zusammenzufassen und mit dem zu kombinieren, was ich weiß, habe ich 10 Hauptmethoden gefunden, um dies zu tun (siehe unten). Außerdem habe ich einige Leistungstests geschrieben (siehe Ergebnisse unten). Wenn wir beispielsweise die Summe aller Schlüssel und Werte einer Karte ermitteln möchten, können wir schreiben:

  1. Verwenden von Iterator und 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. Verwenden von foreach und Map.Entry

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

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

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
  5. Verwenden von keySet und Iterator

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

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
  8. Parallele Verwendung der Java 8 Stream-API

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

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
  10. Verwenden von MutableMap of Eclipse (CS) -Sammlungen

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

Leistungstests (Modus = AverageTime, System = Windows 8.1 64-Bit, Intel i7-4790 3,60 GHz, 16 GB)

  1. Für eine kleine Karte (100 Elemente) ist die Punktzahl 0,308 die beste

    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. Für eine Karte mit 10000 Elementen ist die Punktzahl 37.606 die beste

    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. Für eine Karte mit 100000 Elementen ist die Punktzahl 1184.767 die beste

    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

Diagramme (Leistungstests abhängig von der Kartengröße)

Geben Sie hier die Bildbeschreibung ein

Tabelle (Leistungstests abhängig von der Kartengröße)

          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

Alle Tests sind auf GitHub .

Viacheslav Vedenin
quelle
9
@Viacheslav: sehr schöne Antwort. Ich frage mich nur, wie Java8-APIs in Ihrem Benchmark durch das Erfassen von Lambdas behindert werden ... (z. B. Erfassen long sum = 0; map.forEach( /* accumulate in variable sum*/);des sumLong, was möglicherweise langsamer ist als stream.mapToInt(/*whatever*/).sumbeispielsweise. Natürlich können Sie das Erfassen des Status nicht immer vermeiden, aber dies kann eine vernünftige Ergänzung sein auf die Bank.
GPI
17
Dein 8 Test ist falsch. Es greift ohne Synchronisation von verschiedenen Threads auf dieselbe Variable zu. Wechseln Sie zu AtomicInteger, um das Problem zu lösen.
Talex
44
@ZhekaKozlov: Schau dir die unglaublich großen Fehlerwerte an. Bedenken Sie, dass ein Testergebnis von x±eimpliziert, dass es Ergebnisse innerhalb des Intervalls von x-ebis gab x+e, sodass das schnellste Ergebnis ( 1184.767±332.968) von 852bis reicht 1518, während das zweitlangsamste ( 1706.676±436.867) zwischen 1270und liegt 2144, sodass sich die Ergebnisse immer noch erheblich überlappen. Schauen Sie sich nun das langsamste Ergebnis an 3289.866±1445.564, was bedeutet , dass zwischen 1844und abweichen, 4735und Sie wissen, dass diese Testergebnisse bedeutungslos sind.
Holger
8
Was ist mit dem Vergleich der drei Hauptimplementierungen: HashMap, LinkedHashMap und TreeMap?
Thierry
9
# 1 und # 6 sind genau gleich. Die Verwendung von whilevs. einer forSchleife ist keine andere Technik zum Iterieren. Und ich bin überrascht, dass sie in Ihren Tests solche Unterschiede aufweisen - was darauf hindeutet, dass die Tests nicht ordnungsgemäß von externen Faktoren isoliert sind, die nicht mit den Dingen zusammenhängen, die Sie testen möchten.
ErikE
294

In Java 8 können Sie dies mit den neuen Lambdas-Funktionen sauber und schnell tun:

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

Der Typ von kund vwird vom Compiler abgeleitet und muss nicht Map.Entrymehr verwendet werden.

Kinderleicht!

Der Koordinator
quelle
12
Je nachdem, was Sie mit einer Karte tun möchten, können Sie die Stream-API auch für die von map.entrySet().stream() docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Vitalii Fedorenko
1
Dies funktioniert nicht, wenn Sie auf nicht endgültige Variablen verweisen möchten, die außerhalb Ihres Lambda-Ausdrucks innerhalb von forEach () deklariert wurden ...
Chris
7
@ Chris Richtig. Es funktioniert nicht, wenn Sie versuchen, nicht endgültige Variablen von außerhalb des Lambda effektiv zu verwenden .
Der Koordinator
243

Ja, die Reihenfolge hängt von der spezifischen Map-Implementierung ab.

@ ScArcher2 hat die elegantere Java 1.5-Syntax . In 1.4 würde ich so etwas machen:

Iterator entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Entry thisEntry = (Entry) entries.next();
  Object key = thisEntry.getKey();
  Object value = thisEntry.getValue();
  // ...
}
pkaeding
quelle
41
Bevorzugen Sie die for-Schleife als while .. for (Iterator-Einträge = myMap.entrySet (). Iterator (); entry.hasNext ();) {...} Mit dieser Syntax wird der Bereich 'Einträge' nur auf die for-Schleife reduziert .
Jai
8
@jpredham Sie haben Recht, dass Sie mit dem forKonstrukt as for (Entry e : myMap.entrySet)die Sammlung nicht ändern können, aber das Beispiel, wie @HanuAthena erwähnt hat, sollte funktionieren, da es Ihnen den IteratorUmfang gibt. (Es sei denn, ich vermisse etwas ...)
pkaeding
1
IntelliJ gibt mir Fehler bei Entry thisEntry = (Entry) entries.next();: erkennt nicht Entry. Ist das Pseudocode für etwas anderes?
JohnK
1
@ JohnK versuchen zu importieren java.util.Map.Entry.
pkaeding
1
Diese Lösung funktioniert nicht, wenn Sie einen Ganzzahlschlüssel und einen Zeichenfolgenschlüssel haben.
140

Typischer Code zum Durchlaufen einer Karte ist:

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

HashMapist die kanonische Map-Implementierung und gibt keine Garantien ab (oder sollte die Reihenfolge nicht ändern, wenn keine Mutationsoperation daran ausgeführt wird). SortedMapgibt Einträge zurück, die auf der natürlichen Reihenfolge der Schlüssel basieren, oder aComparator , falls angegeben. LinkedHashMapGibt entweder Einträge in Einfügereihenfolge oder Zugriffsreihenfolge zurück, je nachdem, wie sie erstellt wurden. EnumMapGibt Einträge in natürlicher Reihenfolge der Schlüssel zurück.

(Update: Ich denke, das stimmt nicht mehr. ) Beachten Sie, dass der IdentityHashMap entrySetIterator derzeit eine eigenartige Implementierung hat, die Map.Entryfür jedes Element im entrySet! Die gleiche Instanz zurückgibt. Jedes Mal, wenn ein neuer Iterator voranschreitet, Map.Entrywird der aktualisiert.

Tom Hawtin - Tackline
quelle
6
EnumMap hat auch dieses eigenartige Verhalten zusammen mit IdentityHashMap
Premraj
1
"LinkedHashMap gibt entweder Einträge in [...] Zugriffsreihenfolge [...] zurück" ... also greifen Sie auf die Elemente in der Reihenfolge zu, in der Sie darauf zugreifen? Entweder tautologisch oder etwas Interessantes, das einen Exkurs gebrauchen könnte. ;-)
jpaugh
5
@jpaugh Nur direkter Zugriff auf die LinkedHashMapZählung. Diejenigen , durch iterator, spliterator, entrySetetc., ändern nicht den Auftrag.
Tom Hawtin - Tackline
1
1. obwohlwenn ? 2. Der letzte Absatz könnte von einer Auffrischung profitieren.
Peter Mortensen
122

Beispiel für die Verwendung von Iterator und Generika:

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();
  // ...
}
serg
quelle
14
Sie sollten Iteratoreine for-Schleife einfügen, um den Umfang zu begrenzen.
Steve Kuo
@SteveKuo Was meinst du mit "seinen Umfang einschränken"?
StudioWorks
12
@StudioWorks for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }. Mit diesem Konstrukt beschränken wir den Umfang (Sichtbarkeit der Variablen) entriesauf die for-Schleife.
ComFreek
3
@ ComFreek Oh, ich verstehe. Wusste nicht, dass es so wichtig ist.
StudioWorks
102

Dies ist eine zweiteilige Frage:

Wie man über die Einträge einer Karte iteriert - @ ScArcher2 hat das perfekt beantwortet .

Was ist die Reihenfolge der Iteration? Wenn Sie nur verwenden Map, gibt es streng genommen keine Bestellgarantien . Sie sollten sich also nicht wirklich auf die Reihenfolge verlassen, die von einer Implementierung vorgegeben wird. Die SortedMapSchnittstelle wird jedoch erweitert Mapund bietet genau das, wonach Sie suchen. Implementierungen ergeben immer eine konsistente Sortierreihenfolge.

NavigableMapist eine weitere nützliche Erweiterung - dies ist eine SortedMapmit zusätzlichen Methoden zum Suchen von Einträgen anhand ihrer geordneten Position im Schlüsselsatz. So möglicherweise kann dies die Notwendigkeit entfernen in erster Linie für Iterieren - Sie könnten der Lage sein , die spezifisch finden entrySie nach der Verwendung die sind higherEntry, lowerEntry, ceilingEntry, oder floorEntryMethoden. Die descendingMapMethode bietet Ihnen sogar eine explizite Methode zum Umkehren der Durchlaufreihenfolge .

serg10
quelle
84

Es gibt verschiedene Möglichkeiten, die Karte zu durchlaufen.

Hier ist ein Vergleich ihrer Leistung für einen gemeinsamen Datensatz, der in der Karte gespeichert ist, indem eine Million Schlüsselwertpaare in der Karte gespeichert werden und über die Karte iteriert werden.

1) Verwenden Sie entrySet()in für jede Schleife

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

50 Millisekunden

2) Verwenden Sie keySet()in für jede Schleife

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

76 Millisekunden

3) Verwenden entrySet()und 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 Millisekunden

4) Verwenden keySet()und Iterator

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

75 Millisekunden

Ich habe verwiesen this link.

Darshan Patel
quelle
1
Die Laufzeiten stammen aus dem Artikel, in dem das Java Microbenchmarking Harness nicht verwendet wird. Die Zeiten sind daher unzuverlässig, da der Code beispielsweise vom JIT-Compiler vollständig optimiert worden sein könnte.
AlexB
59

Der richtige Weg, dies zu tun, besteht darin, die akzeptierte Antwort zu verwenden, da sie am effizientesten ist. Ich finde, der folgende Code sieht etwas sauberer aus.

for (String key: map.keySet()) {
   System.out.println(key + "/" + map.get(key));
}
Chris Dail
quelle
15
Dies ist nicht der beste Ansatz, es ist viel effizienter, entrySet () zu verwenden. Findbugs wird diesen Code kennzeichnen (siehe findbugs.sourceforge.net/… )
Jeff Olson
6
@ JeffOlson meh, nicht wirklich. Die Kartensuche ist O (1), sodass sich beide Schleifen gleich verhalten. Zugegeben, in einem Mikro-Benchmark wird es etwas langsamer sein, aber manchmal mache ich das auch, weil ich es hasse, die Typargumente immer wieder zu schreiben. Auch dies wird höchstwahrscheinlich nie Ihr Leistungsengpass sein. Machen Sie es also, wenn der Code dadurch besser lesbar wird.
kritzikratzi
4
detaillierter: O(1) = 2*O(1)ist so ziemlich die Definition der großen O-Notation. Sie haben Recht damit, dass es etwas langsamer läuft, aber in Bezug auf die Komplexität sind sie gleich.
kritzikratzi
2
Mit Kollision oder nicht meine ich, dass es egal ist, ob Sie ein paar Kollisionen haben, offensichtlich ist es eine andere Geschichte, wenn Sie nur Kollisionen haben. Sie sind also ziemlich kleinlich, aber ja, was Sie sagen, ist wahr.
kritzikratzi
2
@ Jeff Olson: Die Kommentare, dass sich die Komplexität von „Big O“ nicht ändert, wenn es nur einen konstanten Faktor gibt, sind richtig. Für mich ist es jedoch wichtig, ob eine Operation eine oder zwei Stunden dauert. Noch wichtiger ist , festzustellen, dass der Faktor betont werden , nicht 2 über eine, wie Iterieren entrySet()überhaupt eine Lookup nicht tragen; Es ist nur eine lineare Durchquerung aller Einträge. Im Gegensatz dazu ist das Durchlaufen der keySet()Suche und das Durchführen einer Suche pro Schlüssel mit einer Suche pro Schlüssel verbunden. Wir sprechen hier also von null Suchvorgängen im Vergleich zu n Suchvorgängen, wobei n die Größe des Schlüssels ist Map. Der Faktor ist also weit darüber hinaus 2
Holger
57

Zu Ihrer Information, Sie können auch verwenden map.keySet()und map.values()wenn Sie nur an Schlüsseln / Werten der Karte interessiert sind und nicht an den anderen.

ckpwong
quelle
42

Mit Java 8 können Sie Map mit forEach- und Lambda-Ausdrücken iterieren.

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

Bei Eclipse-Sammlungen würden Sie die forEachKeyValueMethode für die MapIterableSchnittstelle verwenden, die von den MutableMapund ImmutableMap-Schnittstellen und ihren Implementierungen geerbt wird .

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

Mit einer anonymen inneren Klasse können Sie den Code wie folgt schreiben:

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

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.

Donald Raab
quelle
37

Lambda- Ausdruck Java 8

In Java 1.8 (Java 8) ist dies durch die Verwendung der forEach- Methode aus Aggregatoperationen ( Stream-Operationen ), die Iteratoren aus der Iterable Interface ähnelt, viel einfacher geworden .

Kopieren Sie einfach die folgende Anweisung in Ihren Code und benennen Sie die HashMap- Variable von hm in Ihre HashMap-Variable um, um das Schlüssel-Wert-Paar auszudrucken.

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.

Unten ist der Beispielcode, den ich mit Lambda Expression ausprobiert habe . Dieses Zeug ist so cool. Müssen versuchen.

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

Auch kann man Spliterator für das gleiche verwenden.

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

AKTUALISIEREN


Einschließlich Dokumentationslinks zu Oracle Docs. Weitere Informationen zu Lambda finden Sie unter diesem Link. Lesen Sie Aggregate Operations. Für Spliterator klicken Sie auf diesen Link .

Nitin Mahesh
quelle
36

Theoretisch hängt der effizienteste Weg von der Implementierung von Map ab. Der offizielle Weg, dies zu tun, ist ein Aufruf map.entrySet(), der eine Menge von zurückgibt Map.Entry, von denen jede einen Schlüssel und einen Wert ( entry.getKey()und entry.getValue()) enthält.

In einer eigenwilligen Umsetzung könnte es einen Unterschied machen , ob Sie verwenden map.keySet(), map.entrySet()oder etwas anderes. Aber ich kann mir keinen Grund vorstellen, warum jemand es so schreiben würde. Höchstwahrscheinlich spielt es für die Leistung keine Rolle, was Sie tun.

Und ja, die Reihenfolge hängt von der Implementierung ab - sowie (möglicherweise) von der Reihenfolge der Einfügung und anderen schwer zu kontrollierenden Faktoren.

[edit] Ich habe valueSet()ursprünglich geschrieben, aber natürlich entrySet()ist das eigentlich die Antwort.

Leigh Caldwell
quelle
36

Java 8

Wir haben eine forEachMethode, die einen Lambda-Ausdruck akzeptiert . Wir haben auch Stream- APIs. Betrachten Sie eine Karte:

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

Über Tasten iterieren:

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

Über Werte iterieren:

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

Durch Einträge iterieren (Verwenden von forEach und Streams):

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

Der Vorteil von Streams ist, dass sie einfach parallelisiert werden können, falls wir dies möchten. Wir müssen einfach parallelStream()anstelle von stream()oben verwenden.

forEachOrderedvs forEachmit Streams? Das forEachfolgt nicht der Begegnungsreihenfolge (falls definiert) und ist von Natur aus nicht deterministisch, wo wie das forEachOrderedtut. Dies forEachgarantiert nicht, dass die Bestellung beibehalten wird. Überprüfen Sie dies auch für mehr.

akhil_mittal
quelle
33

Java 8:

Sie können Lambda-Ausdrücke verwenden:

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

Weitere Informationen finden Sie hier .

George Siggouroglou
quelle
@injecteer: Scheint das Motiv von Lambda-Ausdrücken
Humblerookie
9
Sie benötigen keinen Stream, wenn Sie nur eine Karte durchlaufen möchten. myMap.forEach( (currentKey,currentValue) -> /* action */ );ist viel prägnanter.
Holger
29

Versuchen Sie dies mit Java 1.4:

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

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

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

  //...
}
wohnt
quelle
26

In Map kann man über Iteration keysund / oder valuesund / oder both (e.g., entrySet) abhängig von seinem Interesse an_ Like:

  1. Durch keys -> keySet()die Karte iterieren :

    Map<String, Object> map = ...;
    
    for (String key : map.keySet()) {
        //your Business logic...
    }
  2. Durch values -> values()die Karte iterieren :

    for (Object value : map.values()) {
        //your Business logic...
    }
  3. Durch both -> entrySet()die Karte iterieren :

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

Darüber hinaus gibt es drei verschiedene Möglichkeiten, um durch eine HashMap zu iterieren. Sie sind wie folgt__

//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);
}
Rupesh Yadav
quelle
24

Am kompaktesten mit Java 8:

map.entrySet().forEach(System.out::println);
Bluehallu
quelle
21

Wenn Sie eine generische untypisierte Karte haben, können Sie Folgendes verwenden:

Map map = new HashMap();
for (Map.Entry entry : ((Set<Map.Entry>) map.entrySet())) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}
dmunozfer
quelle
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);
        }
    }
}

ODER

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();
        }
    }
}
Fathah Rehman P.
quelle
17

Wenn ich ein Objekt habe, das die Map-Schnittstelle in Java implementiert, und über jedes darin enthaltene Paar iterieren möchte, wie gehe ich dann am effizientesten durch die Map?

Wenn die Effizienz des Schleifens der Schlüssel für Ihre App Priorität hat, wählen Sie eine MapImplementierung, die die Schlüssel in der gewünschten Reihenfolge verwaltet.

Hängt die Reihenfolge der Elemente von der spezifischen Kartenimplementierung ab, die ich für die Schnittstelle habe?

Ja absolut.

  • Einige MapImplementierungen versprechen eine bestimmte Iterationsreihenfolge, andere nicht.
  • Unterschiedliche Implementierungen Mapbehalten unterschiedliche Reihenfolge der Schlüssel-Wert-Paare bei.

In dieser von mir erstellten Tabelle sind die verschiedenen Mapmit Java 11 gebündelten Implementierungen zusammengefasst. Beachten Sie insbesondere die Spalte für die Iterationsreihenfolge . Klicken / tippen Sie zum Zoomen.

Tabelle der Kartenimplementierungen in Java 11 zum Vergleich ihrer Funktionen

Sie können sehen, dass es vier MapImplementierungen gibt, die eine Reihenfolge aufrechterhalten :

  • TreeMap
  • ConcurrentSkipListMap
  • LinkedHashMap
  • EnumMap

NavigableMap Schnittstelle

Zwei davon implementieren die NavigableMapSchnittstelle: TreeMap& ConcurrentSkipListMap.

Die ältere SortedMapSchnittstelle wird effektiv durch die neuere NavigableMapSchnittstelle ersetzt. Möglicherweise finden Sie jedoch Implementierungen von Drittanbietern, die nur die ältere Schnittstelle implementieren.

Natürliche Reihenfolge

Wenn Sie eine möchten Map, deren Paare in der „natürlichen Reihenfolge“ des Schlüssels angeordnet sind, verwenden Sie TreeMapoder ConcurrentSkipListMap. Der Begriff "natürliche Ordnung" bezeichnet die Klasse der Schlüsselgeräte Comparable. Der von der compareToMethode zurückgegebene Wert wird zum Vergleich beim Sortieren verwendet.

Kunden Bestellung

Wenn Sie eine benutzerdefinierte Sortierroutine für Ihre Schlüssel angeben möchten, die zum Verwalten einer sortierten Reihenfolge verwendet werden soll, übergeben Sie eine ComparatorImplementierung, die der Klasse Ihrer Schlüssel entspricht. Verwenden Sie entweder TreeMapoder ConcurrentSkipListMap, indem Sie Ihre übergeben Comparator.

Ursprüngliche Einfügereihenfolge

Wenn Sie möchten, dass die Paare Ihrer Karte in der ursprünglichen Reihenfolge beibehalten werden, in der Sie sie in die Karte eingefügt haben, verwenden Sie LinkedHashMap.

Aufzählungsdefinitionsreihenfolge

Wenn Sie eine Aufzählung wie DayOfWeekoder Monthals Schlüssel verwenden, verwenden Sie die EnumMapKlasse. Diese Klasse ist nicht nur stark optimiert, um sehr wenig Speicher zu verbrauchen und sehr schnell zu laufen, sondern verwaltet Ihre Paare auch in der durch die Aufzählung festgelegten Reihenfolge. Zum DayOfWeekBeispiel wird der Schlüssel von DayOfWeek.MONDAYzuerst gefunden, wenn er iteriert wird, und der Schlüssel von DayOfWeek.SUNDAYwird zuletzt sein.

Andere Überlegungen

MapBerücksichtigen Sie bei der Auswahl einer Implementierung auch Folgendes:

  • NULL. Einige Implementierungen verbieten / akzeptieren NULL als Schlüssel und / oder Wert.
  • Parallelität. Wenn Sie die Zuordnung über mehrere Threads hinweg bearbeiten, müssen Sie eine Implementierung verwenden, die die Parallelität unterstützt. Oder wickeln Sie die Karte mit Collections::synchronizedMap(weniger bevorzugt).

Diese beiden Überlegungen werden in der obigen Grafiktabelle behandelt.

Basil Bourque
quelle
Später Kommentar zu einer Antwort, die auch zu spät zur Party kommt (aber sehr informativ). +1 von mir für die Erwähnung EnumMap, da es das erste Mal ist, dass ich davon gehört habe. Es gibt wahrscheinlich viele Fälle, in denen dies nützlich sein könnte.
user991710
15

Die Reihenfolge hängt immer von der spezifischen Kartenimplementierung ab. Mit Java 8 können Sie eine der folgenden Methoden verwenden:

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

Oder:

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

Das Ergebnis ist das gleiche (gleiche Reihenfolge). Das von der Karte gesicherte entrySet, sodass Sie dieselbe Bestellung erhalten. Das zweite ist praktisch, da Sie damit Lambdas verwenden können, z. B. wenn Sie nur ganzzahlige Objekte drucken möchten, die größer als 5 sind:

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

Der folgende Code zeigt die Iteration durch LinkedHashMap und normale HashMap (Beispiel). Sie werden Unterschiede in der Reihenfolge sehen:

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,

Witold Kaczurba
quelle
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());    
    }
Fadid
quelle
14

Verwenden Sie Java 8:

map.entrySet().forEach(entry -> System.out.println(entry.getValue()));
ABHAY JOHRI
quelle
13

Sie können es mit Generika tun:

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());
}
Pranoti
quelle
12

Eine effektive iterative Lösung über eine Karte ist eine "für jede" Schleife von Java 5 bis Java 7. Hier ist sie:

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

Ab Java 8 können Sie einen Lambda-Ausdruck verwenden, um eine Karte zu durchlaufen. Es ist ein erweitertes "forEach"

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

Wenn Sie eine Bedingung für Lambda schreiben möchten, können Sie diese folgendermaßen schreiben:

phnMap.forEach((k,v)->{
    System.out.println("Key: " + k + " Value: " + v);
    if("abc".equals(k)){
        System.out.println("Hello abc");
    }
});
anandchaugule
quelle
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();

            }
Sajad NasiriNezhad
quelle
10

Es gibt viele Möglichkeiten, dies zu tun. Nachfolgend einige einfache Schritte:

Angenommen, Sie haben eine Karte wie:

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

Dann können Sie wie folgt vorgehen, um Kartenelemente zu durchlaufen.

// ********** 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));
}
Utpal Kumar
quelle
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));
    }
}
Rupendra Sharma
quelle