Si j'ai un objet implémentant l' Map
interface 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?
Réponses:
la source
remove
mé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.map.values()
oumap.keySet()
si vous souhaitez parcourir les valeurs ou les clés uniquement.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:
Utilisation de l' itérateur et de Map.Entry
Utilisation de foreach et Map.Entry
Utilisation de forEach à partir de Java 8
Utilisation de keySet et foreach
Utilisation de keySet et de l' itérateur
Utilisation de for et Map.Entry
Utilisation de l' API Java 8 Stream
Utilisation de l' API Java 8 Stream en parallèle
Utiliser IterableMap de
Apache Collections
Utilisation des collections MutableMap of Eclipse (CS)
Tests de performance (mode = AverageTime, système = Windows 8.1 64 bits, Intel i7-4790 3,60 GHz, 16 Go)
Pour une petite carte (100 éléments), le score de 0,308 est le meilleur
Pour une carte avec 10000 éléments, le score de 37,606 est le meilleur
Pour une carte avec 100 000 éléments, le score de 1184,767 est le meilleur
Graphiques (tests de performances en fonction de la taille de la carte)
Tableau (tests de performances en fonction de la taille de la carte)
Tous les tests sont sur GitHub .
la source
long sum = 0; map.forEach( /* accumulate in variable sum*/);
capture lesum
long, qui peut être plus lent questream.mapToInt(/*whatever*/).sum
par exemple. Bien sûr, vous ne pouvez pas toujours éviter de capturer l'état, mais cela peut être un ajout raisonnable au banc.AtomicInteger
pour résoudre le problème.x±e
implique qu'il y a eu un résultat dans l'intervalle dex-e
àx+e
, donc le résultat le plus rapide (1184.767±332.968
) varie de852
à1518
, tandis que le deuxième plus lent (1706.676±436.867
) s'étend entre1270
et2144
, de sorte que les résultats se chevauchent toujours de manière significative. Regardez maintenant le résultat le plus lent3289.866±1445.564
, ce qui implique une divergence entre1844
et4735
et vous savez que ces résultats de test n'ont aucun sens.while
vs unefor
boucle 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.Dans Java 8, vous pouvez le faire propre et rapide en utilisant les nouvelles fonctionnalités lambdas:
Le type de
k
etv
sera déduit par le compilateur et il n'est plus nécessaire de l'utiliserMap.Entry
.Peasy facile!
la source
map.entrySet().stream()
docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.htmlOui, 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:
la source
for
construction asfor (Entry e : myMap.entrySet)
ne vous permettra pas de modifier la collection, mais l'exemple mentionné par @HanuAthena devrait fonctionner, car il vous donne laIterator
portée. (Sauf si je manque quelque chose ...)Entry thisEntry = (Entry) entries.next();
: ne reconnaît pasEntry
. Est-ce que ce pseudocode est autre chose?java.util.Map.Entry
.Le code typique pour itérer sur une carte est:
HashMap
est 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).SortedMap
retournera les entrées en fonction de l'ordre naturel des clés, ou unComparator
, si fourni.LinkedHashMap
retournera les entrées dans l'ordre d'insertion ou dans l'ordre d'accès selon la façon dont elles ont été construites.EnumMap
renvoie les entrées dans l'ordre naturel des clés.(Mise à jour: je pense que ce n'est plus vrai. ) Remarque, l'
IdentityHashMap
entrySet
itérateur a actuellement une implémentation particulière qui renvoie la mêmeMap.Entry
instance pour chaque élément duentrySet
! Cependant, chaque fois qu'un nouvel itérateur avance, leMap.Entry
est mis à jour.la source
LinkedHashMap
décompte. Ceux à traversiterator
,spliterator
,entrySet
, etc., ne modifie pas l'ordre.Exemple d'utilisation d'itérateur et de génériques:
la source
Iterator
une boucle for pour limiter sa portée.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.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'SortedMap
interface s'étendMap
et fournit exactement ce que vous recherchez - les implémentations donneront toujours un ordre de tri cohérent.NavigableMap
est une autre extension utile - c'est uneSortedMap
avec 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 queentry
vous êtes après avoir utilisé leshigherEntry
,lowerEntry
,ceilingEntry
oufloorEntry
méthodes. LadescendingMap
méthode vous donne même une méthode explicite d' inversion de l'ordre de parcours .la source
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 boucle50 millisecondes
2) Utilisation de
keySet()
in pour chaque boucle76 millisecondes
3) Utilisation
entrySet()
et itérateur50 millisecondes
4) Utilisation
keySet()
et itérateur75 millisecondes
J'en ai parlé
this link
.la source
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.
la source
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.2
, car itérer sur unentrySet()
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 lekeySet()
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 duMap
. Le facteur est donc bien au-delà2
…Pour info, vous pouvez également utiliser
map.keySet()
etmap.values()
si vous êtes uniquement intéressé par les clés / valeurs de la carte et pas l'autre.la source
Avec Java 8 , vous pouvez itérer Map en utilisant les expressions forEach et lambda,
la source
Avec Collections Eclipse , vous pouvez utiliser la
forEachKeyValue
méthode sur l'MapIterable
interface, qui est héritée par lesMutableMap
etImmutableMap
interfaces et leurs mises en œuvre.À l'aide d'une classe interne anonyme, vous pouvez écrire le code comme suit:
Remarque: je suis un committer pour les collections Eclipse.
la source
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.
Voici l'exemple de code que j'ai essayé d'utiliser Lambda Expression . Ce truc est tellement cool. Dois essayer.
On peut également utiliser Spliterator pour la même chose.
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 .
la source
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 deMap.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ûrentrySet()
la réponse.la source
Java 8
Nous avons une
forEach
méthode qui accepte une expression lambda . Nous avons également des API de flux . Considérez une carte:Itérer sur les touches:
Itérer sur les valeurs:
Itérer sur les entrées (en utilisant forEach et Streams):
L'avantage des flux est qu'ils peuvent être parallélisés facilement si nous le souhaitons. Nous devons simplement utiliser
parallelStream()
à la place destream()
ci - dessus.forEachOrdered
vsforEach
avec des flux? LeforEach
ne suit pas l'ordre de rencontre (s'il est défini) et est intrinsèquement non déterministe par nature, tout comme leforEachOrdered
fait.forEach
Ne garantit donc pas que la commande sera conservée. Vérifiez également cela pour en savoir plus.la source
Java 8:
Vous pouvez utiliser des expressions lambda:
Pour plus d'informations, suivez ceci .
la source
myMap.forEach( (currentKey,currentValue) -> /* action */ );
est beaucoup plus concis.Essayez ceci avec Java 1.4:
la source
Dans la carte, on peut itérer sur
keys
et / ouvalues
et / ouboth (e.g., entrySet)
dépend de ce qui est intéressé par_ comme:Itérer à travers
keys -> keySet()
la carte:Itérer à travers
values -> values()
la carte:Itérer à travers
both -> entrySet()
la carte:_De plus, il existe 3 façons différentes d'itérer via un HashMap. Ils sont comme ci-dessous__
la source
Le plus compact avec Java 8:
la source
Si vous avez une carte générique non typée, vous pouvez utiliser:
la source
OU
la source
Si l'efficacité du bouclage des clés est une priorité pour votre application, choisissez une
Map
implémentation qui conserve les clés dans l'ordre souhaité.Oui absolument.
Map
implémentations promettent un certain ordre d'itération, d'autres non.Map
maintenir un ordre différent des paires clé-valeur.Voir ce tableau que j'ai créé résumant les différentes
Map
implémentations fournies avec Java 11. Plus précisément, notez la colonne d' ordre d'itération . Cliquez / appuyez pour zoomer.Vous pouvez voir qu'il existe quatre
Map
implémentations maintenant une commande :TreeMap
ConcurrentSkipListMap
LinkedHashMap
EnumMap
NavigableMap
interfaceDeux d'entre eux implémentent l'
NavigableMap
interface:TreeMap
&ConcurrentSkipListMap
.L'ancienne
SortedMap
interface est effectivement supplantée par la nouvelleNavigableMap
interface. Mais vous pouvez trouver des implémentations tierces implémentant uniquement l'ancienne interface.Ordre naturel
Si vous voulez un
Map
qui garde ses paires organisées par «l'ordre naturel» de la clé, utilisezTreeMap
ouConcurrentSkipListMap
. Le terme «ordre naturel» signifie la classe des outils clésComparable
. 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
Comparator
implémentation appropriée à la classe de vos clés. Utilisez soitTreeMap
ouConcurrentSkipListMap
, en passant votreComparator
.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
DayOfWeek
ouMonth
comme clés, utilisez laEnumMap
classe. 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. PourDayOfWeek
, par exemple, la clé deDayOfWeek.MONDAY
sera 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
Map
implémentation, tenez également compte:Collections::synchronizedMap
(moins préférable).Ces deux considérations sont traitées dans le tableau graphique ci-dessus.
la source
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.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:
Ou:
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:
Le code ci-dessous montre l'itération via LinkedHashMap et HashMap normal (exemple). Vous verrez la différence dans l'ordre:
la source
la source
Utilisez Java 8:
la source
Vous pouvez le faire en utilisant des génériques:
la source
Une solution itérative efficace sur une carte est une boucle «pour chaque» de Java 5 à Java 7. La voici:
Depuis Java 8, vous pouvez utiliser une expression lambda pour parcourir une carte. C'est un «forEach» amélioré
la source
la source
Il existe de nombreuses façons de procéder. Voici quelques étapes simples:
Supposons que vous ayez une carte comme:
Ensuite, vous pouvez faire quelque chose comme ci-dessous pour parcourir les éléments de la carte.
la source
la source