Comment effectuer une itération efficace sur chaque entrée d'une carte Java?

3305

Si j'ai un objet implémentant l' Mapinterface en Java et que je souhaite parcourir chaque paire qu'il contient, quelle est la manière la plus efficace de parcourir la carte?

L'ordre des éléments dépendra-t-il de l'implémentation de carte spécifique que j'ai pour l'interface?

iMack
la source
38
Dans Java 8 à l'aide de Lambda Expression: stackoverflow.com/a/25616206/1503859
Nitin Mahesh
5
Java 8: stackoverflow.com/questions/46898/…
akhil_mittal

Réponses:

5034
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}
ScArcher2
la source
93
Si vous faites cela, cela ne fonctionnera pas car Entry est une classe imbriquée dans Map. java.sun.com/javase/6/docs/api/java/util/Map.html
ScArcher2
266
vous pouvez écrire l'importation comme "import java.util.Map.Entry;" et ça marchera.
jjujuma
55
@Pureferret La seule raison pour laquelle vous voudrez peut-être utiliser un itérateur est si vous devez appeler sa removeméthode. Si tel est le cas, cette autre réponse vous montre comment procéder. Sinon, la boucle améliorée comme indiqué dans la réponse ci-dessus est la voie à suivre.
assylias
102
Je crois que le formulaire Map.Entry est plus clair que l'importation de la classe interne dans l'espace de noms actuel.
Josiah Yoder
31
Notez que vous pouvez utiliser map.values()ou map.keySet()si vous souhaitez parcourir les valeurs ou les clés uniquement.
dguay
1217

Pour résumer les autres réponses et les combiner avec ce que je sais, j'ai trouvé 10 façons principales de le faire (voir ci-dessous). J'ai également écrit des tests de performances (voir les résultats ci-dessous). Par exemple, si nous voulons trouver la somme de toutes les clés et valeurs d'une carte, nous pouvons écrire:

  1. Utilisation de l' itérateur et de 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. Utilisation de foreach et Map.Entry

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

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

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
  5. Utilisation de keySet et de l' itérateur

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

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
  8. Utilisation de l' API Java 8 Stream en parallèle

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

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

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

Tests de performance (mode = AverageTime, système = Windows 8.1 64 bits, Intel i7-4790 3,60 GHz, 16 Go)

  1. Pour une petite carte (100 éléments), le score de 0,308 est le meilleur

    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. Pour une carte avec 10000 éléments, le score de 37,606 est le meilleur

    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. Pour une carte avec 100 000 éléments, le score de 1184,767 est le meilleur

    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

Graphiques (tests de performances en fonction de la taille de la carte)

Entrez la description de l'image ici

Tableau (tests de performances en fonction de la taille de la carte)

          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

Tous les tests sont sur GitHub .

Viacheslav Vedenin
la source
9
@Viacheslav: très belle réponse. Je me demande simplement comment les API Java8 sont gênées, dans votre référence, par la capture de lambdas ... (par exemple, long sum = 0; map.forEach( /* accumulate in variable sum*/);capture le sumlong, qui peut être plus lent que stream.mapToInt(/*whatever*/).sumpar exemple. Bien sûr, vous ne pouvez pas toujours éviter de capturer l'état, mais cela peut être un ajout raisonnable au banc.
GPI
17
Votre 8 test est faux. il accède à la même variable à partir de différents threads sans synchronisation. Passez à AtomicIntegerpour résoudre le problème.
talex
44
@ZhekaKozlov: regardez les valeurs d'erreur incroyablement grandes. Considérez qu'un résultat de test de x±eimplique qu'il y a eu un résultat dans l'intervalle de x-eà x+e, donc le résultat le plus rapide ( 1184.767±332.968) varie de 852à 1518, tandis que le deuxième plus lent ( 1706.676±436.867) s'étend entre 1270et 2144, de sorte que les résultats se chevauchent toujours de manière significative. Regardez maintenant le résultat le plus lent 3289.866±1445.564, ce qui implique une divergence entre 1844et 4735et vous savez que ces résultats de test n'ont aucun sens.
Holger
8
Qu'en est-il de la comparaison des 3 implémentations principales: HashMap, LinkedHashMap et TreeMap?
Thierry
9
# 1 et # 6 sont exactement les mêmes. Utiliser whilevs une forboucle n'est pas une technique différente pour itérer. Et je suis surpris qu'ils aient une telle variation entre eux dans vos tests - ce qui suggère que les tests ne sont pas correctement isolés des facteurs externes sans rapport avec les choses que vous avez l'intention de tester.
ErikE
294

Dans Java 8, vous pouvez le faire propre et rapide en utilisant les nouvelles fonctionnalités 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));

Le type de ket vsera déduit par le compilateur et il n'est plus nécessaire de l'utiliser Map.Entry.

Peasy facile!

Le coordinateur
la source
12
Selon ce que vous voulez faire avec une carte, vous pouvez également utiliser l'API de flux sur les entrées renvoyées par map.entrySet().stream() docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Vitalii Fedorenko
1
Cela ne fonctionnera pas si vous voulez référencer des variables non finales déclarées en dehors de votre expression lambda depuis forEach () ...
Chris
7
@Chris Correct. Cela ne fonctionnera pas si vous essayez d'utiliser efficacement des variables non finales de l'extérieur du lambda.
Le coordinateur
243

Oui, l'ordre dépend de l'implémentation de Map spécifique.

@ ScArcher2 possède la syntaxe Java 1.5 la plus élégante . En 1.4, je ferais quelque chose comme ceci:

Iterator entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Entry thisEntry = (Entry) entries.next();
  Object key = thisEntry.getKey();
  Object value = thisEntry.getValue();
  // ...
}
pkaeding
la source
41
Préfère for-loop que while .. for (Iterator entries = myMap.entrySet (). Iterator (); entries.hasNext ();) {...} Avec cette syntaxe, la portée des 'entrées' est réduite à la boucle for uniquement .
Jai
8
@jpredham Vous avez raison, l'utilisation de la forconstruction as for (Entry e : myMap.entrySet)ne vous permettra pas de modifier la collection, mais l'exemple mentionné par @HanuAthena devrait fonctionner, car il vous donne la Iteratorportée. (Sauf si je manque quelque chose ...)
pkaeding
1
IntelliJ me donne des erreurs sur Entry thisEntry = (Entry) entries.next();: ne reconnaît pas Entry. Est-ce que ce pseudocode est autre chose?
JohnK
1
@JohnK essayez d'importer java.util.Map.Entry.
pkaeding
1
Cette solution ne fonctionnera pas si vous avez une clé entière et une clé chaîne.
140

Le code typique pour itérer sur une carte est:

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

HashMapest l'implémentation de la carte canonique et ne fait aucune garantie (ou bien elle ne devrait pas changer d'ordre si aucune opération de mutation n'est effectuée dessus). SortedMapretournera les entrées en fonction de l'ordre naturel des clés, ou un Comparator, si fourni. LinkedHashMapretournera les entrées dans l'ordre d'insertion ou dans l'ordre d'accès selon la façon dont elles ont été construites. EnumMaprenvoie les entrées dans l'ordre naturel des clés.

(Mise à jour: je pense que ce n'est plus vrai. ) Remarque, l' IdentityHashMap entrySetitérateur a actuellement une implémentation particulière qui renvoie la même Map.Entryinstance pour chaque élément du entrySet! Cependant, chaque fois qu'un nouvel itérateur avance, le Map.Entryest mis à jour.

Tom Hawtin - sellerie
la source
6
EnumMap a également ce comportement particulier avec IdentityHashMap
Premraj
1
"LinkedHashMap retournera les entrées dans [...] l'ordre d'accès [...]" ... pour que vous accédiez aux éléments dans l'ordre dans lequel vous y accédez? Soit tautologique, soit quelque chose d'intéressant qui pourrait utiliser une digression. ;-)
jpaugh
5
@jpaugh Uniquement les accès directs au LinkedHashMapdécompte. Ceux à travers iterator, spliterator, entrySet, etc., ne modifie pas l'ordre.
Tom Hawtin - tackline
1
1. sisi ? 2. Le dernier paragraphe pourrait bénéficier d'une révision.
Peter Mortensen
122

Exemple d'utilisation d'itérateur et de génériques:

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
la source
14
Vous devez mettre Iteratorune boucle for pour limiter sa portée.
Steve Kuo
@SteveKuo Qu'entendez-vous par "limiter sa portée"?
StudioWorks du
12
@StudioWorks for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }. En utilisant cette construction, nous limitons la portée de (visibilité de la variable) entriesà la boucle for.
ComFreek
3
@ComFreek Oh, je vois. Je ne savais pas que cela importait autant.
StudioWorks du
102

C'est une question en deux parties:

Comment parcourir les entrées d'une carte - @ ScArcher2 a parfaitement répondu à cela.

Quel est l'ordre d'itération - si vous utilisez simplement Map, à proprement parler, il n'y a aucune garantie de commande . Vous ne devez donc pas vraiment compter sur l'ordre donné par une implémentation. Cependant, l' SortedMapinterface s'étend Mapet fournit exactement ce que vous recherchez - les implémentations donneront toujours un ordre de tri cohérent.

NavigableMapest une autre extension utile - c'est une SortedMapavec des méthodes supplémentaires pour trouver des entrées par leur position ordonnée dans le jeu de clés. Donc , cela peut potentiellement éliminer la nécessité d'itérer en premier lieu - vous pourriez être en mesure de trouver le spécifique que entryvous êtes après avoir utilisé les higherEntry, lowerEntry, ceilingEntryou floorEntryméthodes. La descendingMapméthode vous donne même une méthode explicite d' inversion de l'ordre de parcours .

serg10
la source
84

Il existe plusieurs façons de parcourir la carte.

Voici une comparaison de leurs performances pour un ensemble de données commun stocké dans la carte en stockant un million de paires de valeurs clés dans la carte et itérera sur la carte.

1) Utilisation de entrySet()in pour chaque boucle

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

50 millisecondes

2) Utilisation de keySet()in pour chaque boucle

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

76 millisecondes

3) Utilisation entrySet()et itérateur

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

50 millisecondes

4) Utilisation keySet()et itérateur

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

75 millisecondes

J'en ai parlé this link.

Darshan Patel
la source
1
Les durées d'exécution sont extraites de l'article, qui n'utilise pas le harnais Java Microbenchmarking. Les temps ne sont donc pas fiables, car le code aurait pu, par exemple, être complètement optimisé par le compilateur JIT.
AlexB
59

La bonne façon de procéder consiste à utiliser la réponse acceptée car elle est la plus efficace. Je trouve que le code suivant semble un peu plus propre.

for (String key: map.keySet()) {
   System.out.println(key + "/" + map.get(key));
}
Chris Dail
la source
15
Ce n'est pas la meilleure approche, il est beaucoup plus efficace d'utiliser le entrySet (). Findbugs signalera ce code (voir findbugs.sourceforge.net/… )
Jeff Olson
6
@JeffOlson meh, pas vraiment. la recherche de carte est O (1) donc les deux boucles se comportent de la même manière. certes, ce sera un peu plus lent dans un micro benchmark mais je le fais parfois aussi parce que je déteste écrire les arguments de type encore et encore. De plus, cela ne sera probablement jamais votre goulot d'étranglement en matière de performances, alors essayez-le s'il rend le code plus lisible.
kritzikratzi
4
plus en détail: O(1) = 2*O(1)est à peu près la définition de la grande notation O. vous avez raison en ce que cela fonctionne un peu plus lentement, mais en termes de complexité, ils sont les mêmes.
kritzikratzi
2
par collision ou non, je voulais dire que peu importe si vous avez quelques collisions, c'est évidemment une autre histoire si vous n'avez que des collisions. donc vous êtes plutôt mesquin, mais oui, ce que vous dites est vrai.
kritzikratzi
2
@Jeff Olson: les commentaires selon lesquels la complexité du «Big O» ne change pas, lorsqu'il n'y a qu'un facteur constant, sont corrects. Pourtant, pour moi, il importe qu'une opération prenne une heure ou deux heures. Plus important encore, il faut souligner que le facteur ne l' est pas2 , car itérer sur un entrySet()ne supporte pas du tout la recherche; c'est juste une traversée linéaire de toutes les entrées. En revanche, itérer sur le keySet()et effectuer une recherche par clé porte une recherche par clé, nous parlons donc de zéro recherche vs n recherche ici, n étant la taille du Map. Le facteur est donc bien au-delà 2
Holger
57

Pour info, vous pouvez également utiliser map.keySet()et map.values()si vous êtes uniquement intéressé par les clés / valeurs de la carte et pas l'autre.

ckpwong
la source
42

Avec Java 8 , vous pouvez itérer Map en utilisant les expressions forEach et lambda,

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

Avec Collections Eclipse , vous pouvez utiliser la forEachKeyValueméthode sur l' MapIterableinterface, qui est héritée par les MutableMapet ImmutableMapinterfaces et leurs mises en œuvre.

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

À l'aide d'une classe interne anonyme, vous pouvez écrire le code comme suit:

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

Remarque: je suis un committer pour les collections Eclipse.

Donald Raab
la source
37

Expression Lambda Java 8

Dans Java 1.8 (Java 8), cela est devenu beaucoup plus facile en utilisant la méthode forEach des opérations d'agrégation ( opérations de flux ) qui ressemble aux itérateurs d' Iterable. Interface.

Copiez simplement coller la déclaration ci-dessous dans votre code et renommez la variable HashMap de hm en votre variable HashMap pour imprimer la paire clé-valeur.

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.

Voici l'exemple de code que j'ai essayé d'utiliser Lambda Expression . Ce truc est tellement cool. Dois essayer.

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

On peut également utiliser Spliterator pour la même chose.

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

MISE À JOUR


Y compris des liens de documentation vers Oracle Docs. Pour plus d'informations sur Lambda, rendez-vous sur ce lien et devez lire Aggregate Operations et pour Spliterator, cliquez sur ce lien .

Nitin Mahesh
la source
36

En théorie, le moyen le plus efficace dépendra de la mise en œuvre de Map. La façon officielle de le faire est d'appeler map.entrySet(), qui renvoie un ensemble de Map.Entry, dont chacun contient une clé et une valeur ( entry.getKey()etentry.getValue() ).

Dans une implémentation idiosyncratique, cela peut faire une différence si vous utilisez map.keySet() ,map.entrySet() ou autre chose. Mais je ne vois pas pourquoi quelqu'un pourrait l'écrire comme ça. Très probablement, cela ne fait aucune différence dans les performances de ce que vous faites.

Et oui, l'ordre dépendra de la mise en œuvre - ainsi que (éventuellement) de l'ordre d'insertion et d'autres facteurs difficiles à contrôler.

[modifier] J'ai écrit à l' valueSet()origine mais c'est bien sûr entrySet()la réponse.

Leigh Caldwell
la source
36

Java 8

Nous avons une forEachméthode qui accepte une expression lambda . Nous avons également des API de flux . Considérez une carte:

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

Itérer sur les touches:

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

Itérer sur les valeurs:

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

Itérer sur les entrées (en utilisant forEach et 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);
        });

L'avantage des flux est qu'ils peuvent être parallélisés facilement si nous le souhaitons. Nous devons simplement utiliserparallelStream() à la place de stream()ci - dessus.

forEachOrderedvs forEachavec des flux? Le forEachne suit pas l'ordre de rencontre (s'il est défini) et est intrinsèquement non déterministe par nature, tout comme le forEachOrderedfait. forEachNe garantit donc pas que la commande sera conservée. Vérifiez également cela pour en savoir plus.

akhil_mittal
la source
33

Java 8:

Vous pouvez utiliser des expressions lambda:

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

Pour plus d'informations, suivez ceci .

George Siggouroglou
la source
@injecteer: Semble le motif des expressions lambda
humblerookie
9
Vous n'avez pas besoin d'un flux si vous souhaitez simplement parcourir une carte. myMap.forEach( (currentKey,currentValue) -> /* action */ );est beaucoup plus concis.
Holger
29

Essayez ceci avec Java 1.4:

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

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

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

  //...
}
abods
la source
26

Dans la carte, on peut itérer sur keyset / ou valueset / ou both (e.g., entrySet) dépend de ce qui est intéressé par_ comme:

  1. Itérer à travers keys -> keySet()la carte:

    Map<String, Object> map = ...;
    
    for (String key : map.keySet()) {
        //your Business logic...
    }
  2. Itérer à travers values -> values()la carte:

    for (Object value : map.values()) {
        //your Business logic...
    }
  3. Itérer à travers both -> entrySet()la carte:

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

_De plus, il existe 3 façons différentes d'itérer via un HashMap. Ils sont comme ci-dessous__

//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
la source
24

Le plus compact avec Java 8:

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

Si vous avez une carte générique non typée, vous pouvez utiliser:

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

OU

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
la source
17

Si j'ai un objet implémentant l'interface Map en Java et que je souhaite parcourir chaque paire qu'il contient, quelle est la manière la plus efficace de parcourir la carte?

Si l'efficacité du bouclage des clés est une priorité pour votre application, choisissez une Mapimplémentation qui conserve les clés dans l'ordre souhaité.

L'ordre des éléments dépendra-t-il de l'implémentation de carte spécifique que j'ai pour l'interface?

Oui absolument.

  • Certains Map implémentations promettent un certain ordre d'itération, d'autres non.
  • Différentes implémentations permettent de Mapmaintenir un ordre différent des paires clé-valeur.

Voir ce tableau que j'ai créé résumant les différentes Mapimplémentations fournies avec Java 11. Plus précisément, notez la colonne d' ordre d'itération . Cliquez / appuyez pour zoomer.

Tableau des implémentations de cartes dans Java 11, comparant leurs fonctionnalités

Vous pouvez voir qu'il existe quatre Mapimplémentations maintenant une commande :

  • TreeMap
  • ConcurrentSkipListMap
  • LinkedHashMap
  • EnumMap

NavigableMap interface

Deux d'entre eux implémentent l' NavigableMapinterface: TreeMap& ConcurrentSkipListMap.

L'ancienne SortedMapinterface est effectivement supplantée par la nouvelle NavigableMapinterface. Mais vous pouvez trouver des implémentations tierces implémentant uniquement l'ancienne interface.

Ordre naturel

Si vous voulez un Mapqui garde ses paires organisées par «l'ordre naturel» de la clé, utilisez TreeMapou ConcurrentSkipListMap. Le terme «ordre naturel» signifie la classe des outils clés Comparable. La valeur retournée par lecompareTo méthode est utilisée pour la comparaison lors du tri.

Commande personnalisée

Si vous souhaitez spécifier une routine de tri personnalisée pour vos clés à utiliser pour maintenir un ordre trié, passez une Comparatorimplémentation appropriée à la classe de vos clés. Utilisez soit TreeMapou ConcurrentSkipListMap, en passant votre Comparator.

Ordre d'insertion d'origine

Si vous souhaitez que les paires de votre carte soient conservées dans leur ordre d'origine dans lequel vous les avez insérées dans la carte, utilisez LinkedHashMap.

Ordre de définition d'énumération

Si vous utilisez une énumération telle que DayOfWeekou Monthcomme clés, utilisez la EnumMapclasse. Non seulement cette classe est hautement optimisée pour utiliser très peu de mémoire et fonctionner très rapidement, mais elle maintient vos paires dans l'ordre défini par l'énumération. Pour DayOfWeek, par exemple, la clé de DayOfWeek.MONDAYsera d'abord trouvée lors de l'itération, et la clé deDayOfWeek.SUNDAY sera la dernière.

Autres considérations

Lors du choix d'une Mapimplémentation, tenez également compte:

  • NULLs. Certaines implémentations interdisent / acceptent un NULL comme clé et / ou valeur.
  • Accès simultané. Si vous manipulez la carte sur des threads, vous devez utiliser une implémentation qui prend en charge la concurrence. Ou envelopper la carte avec Collections::synchronizedMap(moins préférable).

Ces deux considérations sont traitées dans le tableau graphique ci-dessus.

Basil Bourque
la source
Commentaire tardif sur une réponse également tardive à la fête (mais très informative). +1 de ma part pour l'avoir mentionné EnumMap, car c'est la première fois que j'en entends parler. Il y a probablement de nombreux cas où cela pourrait être utile.
user991710
15

L'ordre dépendra toujours de la mise en œuvre de la carte spécifique. En utilisant Java 8, vous pouvez utiliser l'un des deux:

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

Ou:

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

Le résultat sera le même (même ordre). L'entréeSet soutenu par la carte afin que vous obteniez le même ordre. Le second est pratique car il vous permet d'utiliser des lambdas, par exemple si vous ne souhaitez imprimer que des objets entiers supérieurs à 5:

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

Le code ci-dessous montre l'itération via LinkedHashMap et HashMap normal (exemple). Vous verrez la différence dans l'ordre:

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
la source
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
la source
14

Utilisez Java 8:

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

Vous pouvez le faire en utilisant des génériques:

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
la source
12

Une solution itérative efficace sur une carte est une boucle «pour chaque» de Java 5 à Java 7. La voici:

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

Depuis Java 8, vous pouvez utiliser une expression lambda pour parcourir une carte. C'est un «forEach» amélioré

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

Si vous voulez écrire un conditionnel pour lambda, vous pouvez l'écrire comme ceci:

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

Il existe de nombreuses façons de procéder. Voici quelques étapes simples:

Supposons que vous ayez une carte comme:

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

Ensuite, vous pouvez faire quelque chose comme ci-dessous pour parcourir les éléments de la carte.

// ********** 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
la source
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
la source