Se eu tenho um objeto implementando a Map
interface em Java e desejo iterar sobre cada par contido nela, qual é a maneira mais eficiente de percorrer o mapa?
A ordem dos elementos dependerá da implementação específica do mapa que possuo para a interface?
java
dictionary
collections
iteration
iMack
fonte
fonte
Respostas:
fonte
remove
método. Se for esse o caso, esta outra resposta mostra como fazê-lo. Caso contrário, o loop aprimorado, como mostrado na resposta acima, é o caminho a percorrer.map.values()
oumap.keySet()
se desejar percorrer apenas valores ou chaves.Para resumir as outras respostas e combiná-las com o que sei, encontrei 10 maneiras principais de fazer isso (veja abaixo). Além disso, escrevi alguns testes de desempenho (veja os resultados abaixo). Por exemplo, se quisermos encontrar a soma de todas as chaves e valores de um mapa, podemos escrever:
Usando o iterador e Map.Entry
Usando foreach e Map.Entry
Usando o forEach do Java 8
Usando keySet e foreach
Usando keySet e iterador
Usando para e Map.Entry
Usando a API Java 8 Stream
Usando a API Java 8 Stream paralela
Usando IterableMap de
Apache Collections
Usando MutableMap das Coleções Eclipse (CS)
Testes de desempenho (modo = Tempo médio, sistema = Windows 8.1 de 64 bits, Intel i7-4790 3.60 GHz, 16 GB)
Para um mapa pequeno (100 elementos), a pontuação 0,308 é a melhor
Para um mapa com 10000 elementos, a pontuação 37,606 é a melhor
Para um mapa com 100000 elementos, a pontuação 1184.767 é a melhor
Gráficos (testes de desempenho dependendo do tamanho do mapa)
Tabela (testes de desempenho, dependendo do tamanho do mapa)
Todos os testes estão no GitHub .
fonte
long sum = 0; map.forEach( /* accumulate in variable sum*/);
captura osum
longo, que pode ser mais lento do que ostream.mapToInt(/*whatever*/).sum
exemplo, por exemplo. É claro que você nem sempre pode evitar o estado de captura, mas isso pode ser uma adição razoável) para o banco.AtomicInteger
para resolver o problema.x±e
implica que houve resultado dentro do intervalo dex-e
parax+e
, portanto o resultado mais rápido (1184.767±332.968
) varia de852
a1518
, enquanto o segundo mais lento (1706.676±436.867
) é executado entre1270
e2144
, portanto, os resultados ainda se sobrepõem significativamente. Agora olhe para o resultado mais lento,3289.866±1445.564
que implica divergentes entre1844
e4735
e você sabe que esses resultados do teste são sem sentido.while
vs. umfor
loop não é uma técnica diferente para iterar. E estou surpreso que eles tenham essa variação entre eles em seus testes - o que sugere que os testes não são adequadamente isolados de fatores externos não relacionados às coisas que você pretende testar.No Java 8, você pode fazer isso de forma limpa e rápida usando os novos recursos lambdas:
O tipo de
k
ev
será inferido pelo compilador e não há mais necessidade de usarMap.Entry
.Mole-mole!
fonte
map.entrySet().stream()
docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.htmlSim, o pedido depende da implementação específica do mapa.
O @ ScArcher2 possui a sintaxe Java 1.5 mais elegante . Na versão 1.4, eu faria algo assim:
fonte
for
construção comofor (Entry e : myMap.entrySet)
não permitirá modificar a coleção, mas o exemplo mencionado por @HanuAthena deve funcionar, pois fornece oIterator
escopo. (A menos que eu estou faltando alguma coisa ...)Entry thisEntry = (Entry) entries.next();
: não reconheceEntry
. Esse pseudocódigo é para outra coisa?java.util.Map.Entry
.O código típico para iterar sobre um mapa é:
HashMap
é a implementação do mapa canônico e não oferece garantias (ou, embora não deva mudar de ordem, se nenhuma operação de mutação for executada nele).SortedMap
retornará entradas com base na ordem natural das chaves, ou aComparator
, se fornecida.LinkedHashMap
retornará entradas em ordem de inserção ou ordem de acesso, dependendo de como ela foi construída.EnumMap
retorna entradas em ordem natural de chaves.(Atualização: acho que isso não é mais verdade. ) Observe que o
IdentityHashMap
entrySet
iterador atualmente possui uma implementação peculiar que retorna a mesmaMap.Entry
instância para cada item noentrySet
! No entanto, sempre que um novo iterador avança, oMap.Entry
é atualizado.fonte
LinkedHashMap
contagem. Aqueles atravésiterator
,spliterator
,entrySet
, etc., não modifique a ordem.Exemplo de uso do iterador e genéricos:
fonte
Iterator
um loop for para limitar seu escopo.for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }
. Usando essa construção, limitamos o escopo de (visibilidade da variável)entries
ao loop for.Esta é uma questão de duas partes:
Como iterar nas entradas de um mapa - o @ ScArcher2 respondeu perfeitamente.
Qual é a ordem da iteração - se você está apenas usando e
Map
, estritamente falando, não há garantias de pedido . Portanto, você realmente não deve confiar na ordem dada por qualquer implementação. No entanto, aSortedMap
interface se estendeMap
e fornece exatamente o que você está procurando - as implementações sempre fornecerão uma ordem de classificação consistente.NavigableMap
é outra extensão útil - essa é umaSortedMap
com métodos adicionais para localizar entradas pela posição ordenada no conjunto de chaves. Então, potencialmente isso pode eliminar a necessidade de iteração em primeiro lugar - você pode ser capaz de encontrar o específicoentry
que você está depois usando oshigherEntry
,lowerEntry
,ceilingEntry
, oufloorEntry
métodos. OdescendingMap
método ainda fornece um método explícito de reverter a ordem de deslocamento .fonte
Existem várias maneiras de iterar sobre o mapa.
Aqui está a comparação de seus desempenhos para um conjunto de dados comum armazenado no mapa, armazenando um milhão de pares de valores-chave no mapa e iterará sobre o mapa.
1) Usando
entrySet()
para cada loop50 milissegundos
2) Usando
keySet()
para cada loop76 milissegundos
3) Usando
entrySet()
e iterador50 milissegundos
4) Usando
keySet()
e iterador75 milissegundos
Eu me referi
this link
.fonte
A maneira correta de fazer isso é usar a resposta aceita, pois é a mais eficiente. Acho que o código a seguir parece um pouco mais limpo.
fonte
O(1) = 2*O(1)
é praticamente a definição da grande notação O. você está certo no sentido de que é um pouco mais lento, mas em termos de complexidade são os mesmos.2
, pois a iteração sobre umentrySet()
não suporta uma pesquisa; é apenas uma passagem linear de todas as entradas. Por outro lado, iterarkeySet()
e executar uma pesquisa por chave exige uma pesquisa por chave; portanto, estamos falando de zero pesquisas versus n pesquisas aqui, n sendo o tamanho daMap
. Assim, o fator é muito além2
...Para sua informação, você também pode usar
map.keySet()
emap.values()
se estiver interessado apenas nas chaves / valores do mapa e não no outro.fonte
Com o Java 8 , você pode iterar o Mapa usando a expressão forEach e lambda,
fonte
Com Eclipse coleções , você usaria o
forEachKeyValue
método naMapIterable
interface, que é herdado pelosMutableMap
eImmutableMap
interfaces e suas implementações.Usando uma classe interna anônima, você pode escrever o código da seguinte maneira:
Nota: Sou um colaborador das Coleções Eclipse.
fonte
No Java 1.8 (Java 8), isso se tornou muito mais fácil usando o método forEach das operações agregadas ( operações de fluxo ) que se parecem com os iteradores da Iterable Interface.
Apenas copie a instrução colar abaixo para o seu código e renomeie a variável HashMap de hm para sua variável HashMap para imprimir o par de valores-chave.
Abaixo está o código de exemplo que eu tentei usar a expressão Lambda . Esse material é tão legal. Deve tentar.
Também é possível usar o Spliterator para o mesmo.
ATUALIZAR
Incluindo links de documentação para o Oracle Docs. Para saber mais sobre o Lambda, acesse este link e leia Aggregate Operations e, para o Spliterator, acesse este link .
fonte
Em teoria, a maneira mais eficiente dependerá de qual implementação do Map. A maneira oficial de fazer isso é chamar
map.entrySet()
, que retorna um conjunto deMap.Entry
, cada um dos quais contém uma chave e um valor (entry.getKey()
eentry.getValue()
).Em uma implementação idiossincráticas, pode fazer alguma diferença se você usa
map.keySet()
,map.entrySet()
ou qualquer outra coisa. Mas não consigo pensar em uma razão pela qual alguém escreveria assim. Muito provavelmente, não faz diferença para o desempenho do que você faz.E sim, o pedido dependerá da implementação - bem como (possivelmente) o pedido de inserção e outros fatores difíceis de controlar.
[editar] Eu escrevi
valueSet()
originalmente, masentrySet()
é claro que é realmente a resposta.fonte
Java 8
Temos um
forEach
método que aceita uma expressão lambda . Também temos APIs de fluxo . Considere um mapa:Iterar sobre chaves:
Iterar sobre valores:
Iterar sobre entradas (Usando forEach e Streams):
A vantagem dos fluxos é que eles podem ser facilmente paralelizados caso desejemos. Nós simplesmente precisamos usar
parallelStream()
no lugar dostream()
acima.forEachOrdered
vsforEach
com fluxos? OforEach
não segue a ordem do encontro (se definido) e é inerentemente não determinístico por natureza, ondeforEachOrdered
ocorre. PortantoforEach
, não garante que o pedido seja mantido. Além disso, verifique isso para mais.fonte
Java 8:
Você pode usar expressões lambda:
Para mais informações, siga isto .
fonte
myMap.forEach( (currentKey,currentValue) -> /* action */ );
é muito mais conciso.Tente isso com o Java 1.4:
fonte
No mapa, é possível fazer uma iteração sobre
keys
e / ouvalues
e / ouboth (e.g., entrySet)
depende do interesse de alguém_ Como:Iterar através
keys -> keySet()
do mapa:Iterar através
values -> values()
do mapa:Iterar através
both -> entrySet()
do mapa:_ Além disso, existem três maneiras diferentes de Iterar Através de um HashMap. Eles são como abaixo__
fonte
Mais compacto com o Java 8:
fonte
Se você possui um mapa genérico sem tipo, pode usar:
fonte
OU
fonte
Se a eficiência do loop das chaves for uma prioridade para seu aplicativo, escolha uma
Map
implementação que mantenha as chaves na ordem desejada.Sim absolutamente.
Map
implementações prometem uma certa ordem de iteração, outras não.Map
manutenção de diferentes ordens dos pares de valores-chave.Consulte esta tabela que criei resumindo as várias
Map
implementações incluídas no Java 11. Especificamente, observe a coluna da ordem de iteração . Clique / toque para ampliar.Você pode ver que há quatro
Map
implementações mantendo um pedido :TreeMap
ConcurrentSkipListMap
LinkedHashMap
EnumMap
NavigableMap
interfaceDois deles implementam a
NavigableMap
interface:TreeMap
&ConcurrentSkipListMap
.A
SortedMap
interface mais antiga é efetivamente substituída pelaNavigableMap
interface mais nova . Mas você pode encontrar implementações de terceiros implementando apenas a interface mais antiga.Ordem natural
Se você deseja um
Map
que mantenha seus pares organizados pela "ordem natural" da tecla, useTreeMap
ouConcurrentSkipListMap
. O termo "ordem natural" significa a classe das chaves implementadasComparable
. O valor retornado pelocompareTo
método é usado para comparação na classificação.Pedido personalizado
Se você desejar especificar uma rotina de classificação personalizada para suas chaves serem usadas na manutenção de uma ordem classificada, passe uma
Comparator
implementação apropriada para a classe de suas chaves. Use umTreeMap
ouConcurrentSkipListMap
, passando seuComparator
.Pedido de inserção original
Se você deseja que os pares do seu mapa sejam mantidos na ordem original em que os inseriu no mapa, use
LinkedHashMap
.Ordem de definição enum
Se você estiver usando uma enumeração como
DayOfWeek
ouMonth
como suas chaves, use aEnumMap
classe Essa classe não é apenas altamente otimizada para usar muito pouca memória e executar muito rápido, mas também mantém seus pares na ordem definida pelo enum. PorDayOfWeek
exemplo, a chave deDayOfWeek.MONDAY
será encontrada primeiro quando iterada e a chave deDayOfWeek.SUNDAY
será a última.Outras considerações
Ao escolher uma
Map
implementação, considere também:Collections::synchronizedMap
(menos preferível).Ambas as considerações são abordadas na tabela gráfica acima.
fonte
EnumMap
, já que é a primeira vez que ouvi falar disso. Provavelmente há muitos casos em que isso pode ser útil.A ordem sempre dependerá da implementação específica do mapa. Usando o Java 8, você pode usar um destes:
Ou:
O resultado será o mesmo (mesma ordem). O entrySet é apoiado pelo mapa para que você obtenha o mesmo pedido. O segundo é útil, pois permite o uso de lambdas, por exemplo, se você deseja imprimir apenas objetos Inteiros maiores que 5:
O código abaixo mostra a iteração através do LinkedHashMap e do HashMap normal (exemplo). Você verá a diferença na ordem:
fonte
fonte
Use o Java 8:
fonte
Você pode fazer isso usando genéricos:
fonte
Uma solução iterativa eficaz sobre um mapa é um loop 'para cada' do Java 5 ao Java 7. Aqui está:
No Java 8, você pode usar uma expressão lambda para iterar sobre um mapa. É um 'forEach' aprimorado
fonte
fonte
Existem várias maneiras de fazer isso. Abaixo estão alguns passos simples:
Suponha que você tenha um mapa como:
Depois, você pode fazer algo como o abaixo para percorrer os elementos do mapa.
fonte
fonte