Si tengo un objeto que implementa la Map
interfaz 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
remove
mé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 Collections
Uso 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 elsum
largo, que puede ser más lento de lo que se dice,stream.mapToInt(/*whatever*/).sum
por ejemplo. Por supuesto, no siempre puede evitar capturar el estado, pero eso puede ser una adición razonable a la banca.AtomicInteger
para resolver el problema.x±e
implica que hubo un resultado dentro del intervalo dex-e
ax+e
, por lo que el resultado más rápido (1184.767±332.968
) varía de852
a1518
, mientras que el segundo más lento (1706.676±436.867
) se ejecuta entre1270
y2144
, por lo que los resultados aún se superponen significativamente. Ahora mire el resultado más lento,3289.866±1445.564
que implica divergentes entre1844
y4735
y usted sabe que estos resultados de la prueba no tienen sentido.while
vs. unfor
bucle 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
k
yv
será 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
for
construcció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 elIterator
alcance. (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:
HashMap
es 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).SortedMap
devolverá entradas según el orden natural de las claves, o aComparator
, si se proporciona.LinkedHashMap
devolverá entradas en orden de inserción u orden de acceso dependiendo de cómo se haya construido.EnumMap
devuelve entradas en el orden natural de las claves.(Actualización: creo que esto ya no es cierto. ) Tenga en cuenta que el
IdentityHashMap
entrySet
iterador actualmente tiene una implementación peculiar que devuelve la mismaMap.Entry
instancia para cada elemento en elentrySet
! Sin embargo, cada vez que un nuevo iterador avanza,Map.Entry
se actualiza.fuente
LinkedHashMap
conteo. Aquellos a través deiterator
,spliterator
,entrySet
, etc, no modifique la orden.Ejemplo de uso de iterador y genéricos:
fuente
Iterator
un 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)entries
al 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, laSortedMap
interfaz se extiendeMap
y proporciona exactamente lo que está buscando: las implementaciones siempre darán un orden de clasificación consistente.NavigableMap
es otra extensión útil : se trata deSortedMap
mé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íficoentry
que está después usando loshigherEntry
,lowerEntry
,ceilingEntry
, ofloorEntry
métodos. EldescendingMap
mé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
forEachKeyValue
método en laMapIterable
interfaz, que se hereda por losMutableMap
y lasImmutableMap
interfaces 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
forEach
mé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.forEachOrdered
vsforEach
con transmisiones? ElforEach
no sigue el orden de encuentro (si está definido) y es inherentemente no determinista en la naturaleza donde loforEachOrdered
hace. PorforEach
lo 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
keys
y / ovalues
y / 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
Map
implementación que mantenga las claves en el orden deseado.Si, absolutamente.
Map
implementaciones prometen un cierto orden de iteración, otras no.Map
mantener un orden diferente de los pares clave-valor.Vea esta tabla que creé resumiendo las diversas
Map
implementaciones incluidas con Java 11. Específicamente, observe la columna de orden de iteración . Haga clic / toque para ampliar.Puede ver que hay cuatro
Map
implementaciones que mantienen un orden :TreeMap
ConcurrentSkipListMap
LinkedHashMap
EnumMap
NavigableMap
interfazDos de ellos implementan la
NavigableMap
interfaz:TreeMap
&ConcurrentSkipListMap
.La
SortedMap
interfaz más antigua es suplantada efectivamente por laNavigableMap
interfaz más nueva . Pero puede encontrar implementaciones de terceros que implementan solo la interfaz anterior.Orden natural
Si desea un
Map
que mantenga sus pares ordenados por el "orden natural" de la clave, useTreeMap
oConcurrentSkipListMap
. El término "orden natural" significa la clase de los implementos de clavesComparable
. El valor devuelto por elcompareTo
mé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
Comparator
implementación adecuada a la clase de sus claves. Use cualquieraTreeMap
oConcurrentSkipListMap
, 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
DayOfWeek
oMonth
como sus claves, use laEnumMap
clase 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. PorDayOfWeek
ejemplo, la clave deDayOfWeek.MONDAY
se encontrará primero cuando se itera, y la clave deDayOfWeek.SUNDAY
será la última.Otras Consideraciones
Al elegir una
Map
implementació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