Si tengo un objeto que implementa la Mapinterfaz en Java y deseo iterar sobre cada par que contiene, ¿cuál es la forma más eficiente de recorrer el mapa?
¿El orden de los elementos dependerá de la implementación específica del mapa que tenga para la interfaz?
java
dictionary
collections
iteration
iMack
fuente
fuente

Respuestas:
fuente
removemétodo. Si ese es el caso, esta otra respuesta le muestra cómo hacerlo. De lo contrario, el bucle mejorado como se muestra en la respuesta anterior es el camino a seguir.map.values()omap.keySet()si desea recorrer solo valores o claves.Para resumir las otras respuestas y combinarlas con lo que sé, encontré 10 formas principales de hacerlo (ver más abajo). Además, escribí algunas pruebas de rendimiento (ver los resultados a continuación). Por ejemplo, si queremos encontrar la suma de todas las claves y valores de un mapa, podemos escribir:
Usando iterador y Map.Entry
Usando foreach y Map.Entry
Usando forEach desde Java 8
Usando keySet y foreach
El uso conjunto de claves y iterador
Usando para y Map.Entry
Usando la API Java 8 Stream
Usando el Java 8 Stream API paralelo
Usando IterableMap de
Apache CollectionsUso de colecciones MutableMap de Eclipse (CS)
Pruebas de rendimiento (modo = Tiempo promedio, sistema = Windows 8.1 de 64 bits, Intel i7-4790 3.60 GHz, 16 GB)
Para un mapa pequeño (100 elementos), el puntaje 0.308 es el mejor
Para un mapa con 10000 elementos, el puntaje 37.606 es el mejor
Para un mapa con 100000 elementos, el puntaje 1184.767 es el mejor
Gráficos (pruebas de rendimiento según el tamaño del mapa)
Tabla (pruebas de rendimiento según el tamaño del mapa)
Todas las pruebas están en GitHub .
fuente
long sum = 0; map.forEach( /* accumulate in variable sum*/);captura elsumlargo, que puede ser más lento de lo que se dice,stream.mapToInt(/*whatever*/).sumpor ejemplo. Por supuesto, no siempre puede evitar capturar el estado, pero eso puede ser una adición razonable a la banca.AtomicIntegerpara resolver el problema.x±eimplica que hubo un resultado dentro del intervalo dex-eax+e, por lo que el resultado más rápido (1184.767±332.968) varía de852a1518, mientras que el segundo más lento (1706.676±436.867) se ejecuta entre1270y2144, por lo que los resultados aún se superponen significativamente. Ahora mire el resultado más lento,3289.866±1445.564que implica divergentes entre1844y4735y usted sabe que estos resultados de la prueba no tienen sentido.whilevs. unforbucle no es una técnica diferente para iterar. Y me sorprende que tengan tanta variación entre ellos en sus pruebas, lo que sugiere que las pruebas no están aisladas adecuadamente de factores externos no relacionados con las cosas que pretende probar.En Java 8 puede hacerlo limpio y rápido utilizando las nuevas características lambdas:
El tipo de
kyvserá inferido por el compilador y ya no hay necesidad de usarloMap.Entry.¡Pan comido!
fuente
map.entrySet().stream()docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.htmlSí, el orden depende de la implementación específica del mapa.
@ ScArcher2 tiene la sintaxis Java 1.5 más elegante . En 1.4, haría algo como esto:
fuente
forconstrucción comofor (Entry e : myMap.entrySet)no le permitirá modificar la colección, pero el ejemplo como @HanuAthena mencionó que debería funcionar, ya que le da elIteratoralcance. (A menos que me falte algo ...)Entry thisEntry = (Entry) entries.next();: no reconoceEntry. ¿Es ese seudocódigo para otra cosa?java.util.Map.Entry.El código típico para iterar sobre un mapa es:
HashMapes la implementación del mapa canónico y no ofrece garantías (o aunque no debería cambiar el orden si no se realiza ninguna operación de mutación).SortedMapdevolverá entradas según el orden natural de las claves, o aComparator, si se proporciona.LinkedHashMapdevolverá entradas en orden de inserción u orden de acceso dependiendo de cómo se haya construido.EnumMapdevuelve entradas en el orden natural de las claves.(Actualización: creo que esto ya no es cierto. ) Tenga en cuenta que el
IdentityHashMapentrySetiterador actualmente tiene una implementación peculiar que devuelve la mismaMap.Entryinstancia para cada elemento en elentrySet! Sin embargo, cada vez que un nuevo iterador avanza,Map.Entryse actualiza.fuente
LinkedHashMapconteo. Aquellos a través deiterator,spliterator,entrySet, etc, no modifique la orden.Ejemplo de uso de iterador y genéricos:
fuente
Iteratorun bucle for para limitar su alcance.for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }. Al usar esa construcción, limitamos el alcance de (visibilidad de la variable)entriesal bucle for.Esta es una pregunta de dos partes:
Cómo iterar sobre las entradas de un Mapa : @ ScArcher2 lo ha respondido perfectamente.
¿Cuál es el orden de iteración ? Si solo está usando
Map, estrictamente hablando, no hay garantías de pedido . Por lo tanto, no debe confiar realmente en el orden dado por ninguna implementación. Sin embargo, laSortedMapinterfaz se extiendeMapy proporciona exactamente lo que está buscando: las implementaciones siempre darán un orden de clasificación consistente.NavigableMapes otra extensión útil : se trata deSortedMapmétodos adicionales para buscar entradas por su posición ordenada en el conjunto de claves. Por lo tanto, potencialmente esto puede eliminar la necesidad de la iteración en el primer lugar - que podría ser capaz de encontrar el específicoentryque está después usando loshigherEntry,lowerEntry,ceilingEntry, ofloorEntrymétodos. EldescendingMapmétodo incluso le brinda un método explícito para revertir el orden transversal .fuente
Hay varias formas de iterar sobre el mapa.
Aquí hay una comparación de sus rendimientos para un conjunto de datos común almacenado en el mapa almacenando un millón de pares de valores clave en el mapa e iterará sobre el mapa.
1) Usando
entrySet()en cada bucle50 milisegundos
2) Usando
keySet()en cada bucle76 milisegundos
3) Uso
entrySet()e iterador50 milisegundos
4) Uso
keySet()e iterador75 milisegundos
Me he referido
this link.fuente
La forma correcta de hacer esto es usar la respuesta aceptada, ya que es la más eficiente. Encuentro que el siguiente código se ve un poco más limpio.
fuente
O(1) = 2*O(1)es más o menos la definición de la notación O grande. tienes razón en que funciona un poco más lento, pero en términos de complejidad son lo mismo.2, ya que iterar sobre unentrySet()no tiene ninguna búsqueda; Es solo un recorrido lineal de todas las entradas. En contraste, iterar sobrekeySet()y realizar una búsqueda por tecla conlleva una búsqueda por tecla, por lo que estamos hablando de cero búsquedas frente a n búsquedas aquí, siendo n el tamaño de laMap. Así que el factor está mucho más allá2...Para su información, también puede usar
map.keySet()ymap.values()si solo le interesan las claves / valores del mapa y no el otro.fuente
Con Java 8 , puede iterar Map usando forEach y la expresión lambda,
fuente
Con Eclipse Colecciones , utilizaría el
forEachKeyValuemétodo en laMapIterableinterfaz, que se hereda por losMutableMapy lasImmutableMapinterfaces y sus implementaciones.Usando una clase interna anónima, puede escribir el código de la siguiente manera:
Nota: Soy un committer para Eclipse Collections.
fuente
En Java 1.8 (Java 8), esto se ha vuelto mucho más fácil mediante el uso de cada método de las operaciones de agregado ( operaciones de flujo ) que se parece a los iteradores de la interfaz Iterable .
Simplemente copie la declaración pegar debajo de su código y cambie el nombre de la variable HashMap de hm a su variable HashMap para imprimir el par clave-valor.
A continuación se muestra el código de muestra que intenté usar Lambda Expression . Esto es genial. Deberías intentar.
También se puede usar Spliterator para lo mismo.
ACTUALIZAR
Incluyendo enlaces de documentación a Oracle Docs. Para obtener más información sobre Lambda, vaya a este enlace y debe leer Operaciones agregadas y para Spliterator, vaya a este enlace .
fuente
En teoría, la forma más eficiente dependerá de la implementación de Map. La forma oficial de hacer esto es llamar
map.entrySet(), que devuelve un conjunto deMap.Entry, cada uno de los cuales contiene una clave y un valor (entry.getKey()yentry.getValue()).En una implementación idiosincrásica, podría tener alguna diferencia si se utiliza
map.keySet(),map.entrySet()o alguna otra cosa. Pero no puedo pensar en una razón por la que alguien lo escribiría así. Lo más probable es que no influya en el rendimiento lo que haces.Y sí, el orden dependerá de la implementación, así como (posiblemente) el orden de inserción y otros factores difíciles de controlar.
[editar] Escribí
valueSet()originalmente pero, por supuesto, enentrySet()realidad es la respuesta.fuente
Java 8
Tenemos un
forEachmétodo que acepta una expresión lambda . También tenemos API de transmisión . Considere un mapa:Iterar sobre las teclas:
Iterar sobre valores:
Iterar sobre entradas (Usar para cada uno y Streams):
La ventaja de las transmisiones es que pueden paralelizarse fácilmente en caso de que lo deseemos. Simplemente necesitamos usar
parallelStream()en lugar de lostream()anterior.forEachOrderedvsforEachcon transmisiones? ElforEachno sigue el orden de encuentro (si está definido) y es inherentemente no determinista en la naturaleza donde loforEachOrderedhace. PorforEachlo tanto , no garantiza que se mantenga el pedido. También verifique esto para más.fuente
Java 8:
Puedes usar expresiones lambda:
Para más información, sigue esto .
fuente
myMap.forEach( (currentKey,currentValue) -> /* action */ );Es mucho más conciso.Prueba esto con Java 1.4:
fuente
En el mapa, se puede repetir
keysy / ovaluesy / oboth (e.g., entrySet)depender de uno interesado en: Me gusta:Iterar a través
keys -> keySet()del mapa:Iterar a través
values -> values()del mapa:Iterar a través
both -> entrySet()del mapa:_ Además, hay 3 formas diferentes de iterar a través de un HashMap. Son como abajo__
fuente
Más compacto con Java 8:
fuente
Si tiene un mapa genérico sin tipo, puede usar:
fuente
O
fuente
Si la eficiencia del bucle de las claves es una prioridad para su aplicación, elija una
Mapimplementación que mantenga las claves en el orden deseado.Si, absolutamente.
Mapimplementaciones prometen un cierto orden de iteración, otras no.Mapmantener un orden diferente de los pares clave-valor.Vea esta tabla que creé resumiendo las diversas
Mapimplementaciones incluidas con Java 11. Específicamente, observe la columna de orden de iteración . Haga clic / toque para ampliar.Puede ver que hay cuatro
Mapimplementaciones que mantienen un orden :TreeMapConcurrentSkipListMapLinkedHashMapEnumMapNavigableMapinterfazDos de ellos implementan la
NavigableMapinterfaz:TreeMap&ConcurrentSkipListMap.La
SortedMapinterfaz más antigua es suplantada efectivamente por laNavigableMapinterfaz más nueva . Pero puede encontrar implementaciones de terceros que implementan solo la interfaz anterior.Orden natural
Si desea un
Mapque mantenga sus pares ordenados por el "orden natural" de la clave, useTreeMapoConcurrentSkipListMap. El término "orden natural" significa la clase de los implementos de clavesComparable. El valor devuelto por elcompareTométodo se utiliza para comparar en la clasificación.Orden personalizado
Si desea especificar una rutina de clasificación personalizada para que sus claves se usen para mantener un orden ordenado, pase una
Comparatorimplementación adecuada a la clase de sus claves. Use cualquieraTreeMapoConcurrentSkipListMap, pasando suComparator.Orden de inserción original
Si desea que los pares de su mapa se mantengan en su orden original en el que los insertó en el mapa, use
LinkedHashMap.Orden de definición de enumeración
Si está utilizando una enumeración como
DayOfWeekoMonthcomo sus claves, use laEnumMapclase Esta clase no solo está altamente optimizada para usar muy poca memoria y ejecutarse muy rápido, sino que mantiene sus pares en el orden definido por la enumeración. PorDayOfWeekejemplo, la clave deDayOfWeek.MONDAYse encontrará primero cuando se itera, y la clave deDayOfWeek.SUNDAYserá la última.Otras Consideraciones
Al elegir una
Mapimplementación, también considere:Collections::synchronizedMap(menos preferible).Ambas consideraciones están cubiertas en la tabla gráfica anterior.
fuente
EnumMap, ya que es la primera vez que escucho de él. Probablemente hay muchos casos en los que esto podría ser útil.El orden siempre dependerá de la implementación específica del mapa. Usando Java 8 puede usar cualquiera de estos:
O:
El resultado será el mismo (mismo orden). La entrada está respaldada por el mapa para que obtenga el mismo orden. El segundo es útil ya que le permite usar lambdas, por ejemplo, si solo desea imprimir solo objetos enteros que sean mayores que 5:
El siguiente código muestra la iteración a través de LinkedHashMap y HashMap normal (ejemplo). Verá la diferencia en el orden:
fuente
fuente
Utiliza Java 8:
fuente
Puedes hacerlo usando genéricos:
fuente
Una solución iterativa efectiva sobre un mapa es un 'para cada' bucle desde Java 5 hasta Java 7. Aquí está:
Desde Java 8 puede usar una expresión lambda para iterar sobre un Mapa. Es un 'forEach' mejorado
fuente
fuente
Hay muchas formas de hacer esto. A continuación hay algunos pasos simples:
Supongamos que tiene un mapa como:
Luego puede hacer algo como lo siguiente para iterar sobre los elementos del mapa.
fuente
fuente