Estou aprendendo sobre HashMaps em Java e estou confuso sobre a ordem de iteração. A documentação afirma que HashMap não garante nenhuma ordem de iteração específica, mas no meu teste simples, a ordem parece permanecer consistente:
Esta classe não garante a ordem do mapa; em particular, não garante que a ordem permanecerá constante ao longo do tempo. Documentação Java 11
import java.util.HashMap;
public class HashMapDemo {
public static void main(String[] args) {
HashMap<String, String> dishes = new HashMap<>();
// Adding more elements with complex keys
dishes.put("dish-1234", "Pho");
dishes.put("dish-5678", "Spicy Beef Noodle Soup");
dishes.put("dish-9012", "Broken Rice");
dishes.put("dish-3456", "Banh Mi");
dishes.put("dish-7890", "Hu Tieu");
dishes.put("dish-2345", "Mi Quang");
dishes.put("dish-6789", "Crab Noodle Soup");
dishes.put("dish-0123", "Rolled Rice Cake");
System.out.println("First time:");
dishes.forEach((id, name) -> System.out.println(id + ": " + name));
// Create new HashMap with same data
HashMap<String, String> dishes2 = new HashMap<>();
dishes2.putAll(dishes);
System.out.println("\nSecond time (New HashMap):");
dishes2.forEach((id, name) -> System.out.println(id + ": " + name));
}
}
Saída:
First time:
dish-7890: Hu Tieu
dish-3456: Banh Mi
dish-2345: Mi Quang
dish-1234: Pho
dish-0123: Rolled Rice Cake
dish-5678: Spicy Beef Noodle Soup
dish-9012: Broken Rice
dish-6789: Crab Noodle Soup
Second time (New HashMap):
dish-7890: Hu Tieu
dish-3456: Banh Mi
dish-2345: Mi Quang
dish-1234: Pho
dish-0123: Rolled Rice Cake
dish-5678: Spicy Beef Noodle Soup
dish-9012: Broken Rice
dish-6789: Crab Noodle Soup
Entendo que se eu precisar de ordem garantida, devo usar LinkedHashMap ou TreeMap, mas estou tentando entender o comportamento real do HashMap. Li alguma documentação falando sobre redimensionamento e re-hash, talvez seja muito difícil de entender para mim.
Também li alguns posts falando desse problema, mas não consigo reproduzir
- Por que o HashMap não garante que a ordem do mapa permanecerá constante ao longo do tempo
- Causa da diferença na ordem de inserção de HashMap e LinkedHashMap
Como posso entender isso?
O comportamento pode variar
Você está tentando entender os detalhes de implementação interna de
HashMap
. Não faça isso.Qualquer comportamento que você observe pode variar.
O Javadoc é o contrato
Se o Javadoc disser que você não pode contar com uma ordem específica, então não dependa de uma ordem específica.
Se o Javadoc não promete segurança para threads, então não espere segurança para threads.
O Javadoc é o contrato formal, o acordo entre você e os programadores de implementação. Não faça suposições, não use sua intuição. O que você lê no Javadoc descreve o comportamento esperado. Qualquer coisa além do Javadoc pode existir, pode não existir ou pode variar, mas nunca deve ser esperado por você.
SequencedMap
Para uma ordem específica, use uma implementação de
SequencedMap
. Implementações agrupadas com Java:ConcurrentSkipListMap
,LinkedHashMap
, eTreeMap
. Para saber mais sobre a API Sequenced Collections adicionada ao Java 21, leia JEP 431 e veja a excelente palestra de Stuart Marks .Java oferece duas outras interfaces de mapa ordenado:
NavigableMap
(Java 6+) eSortedMap
(Java 2+), ambas implementadas por duas das três classes nomeadas acima:ConcurrentSkipListMap
eTreeMap
.Considere usar
TreeMap
primeiro, se thread-safety não for uma preocupação. Grandes quantidades de dados podem ter melhor desempenho comConcurrentSkipListMap
.Codificação de sereia:
Terceiros
Você pode encontrar implementações de terceiros de mapas ordenados, como no Google Guava ou no Eclipse Collections .
Curiosidade
Se a curiosidade o levar a aprender sobre os detalhes da implementação do
HashMap
, aprenda os conceitos básicos como na Wikipedia e depois estude o código-fonte aberto no GitHub .Isso é explicável. Primeiro, alguns fatos relevantes:
Você está usando um tipo de chave (
String
) que tem umhashCode
método cujo algoritmo é especificado e que tem a garantia de fornecer o mesmo valor de hashcode para todas as execuções 1 .Você está inserindo os mesmos pares de chave/valor exatamente na mesma ordem todas as vezes.
Dado o acima, a implementação do
HashMap
que você está usando vai lhe dar uma ordem consistente para as chaves. Se você estivesse preparado para analisar o código-fonte, você seria capaz de ver o porquê.No entanto, isso é um artefato da implementação atual... e da maneira como você a está usando.
Se você alternar para uma JVM diferente com uma
HashMap
implementação diferente, poderá obter uma ordenação diferente. (Especialmente se você inserir muito mais chaves distintas.)Se você alterasse a chave para um tipo que
hashCode
pudesse ser diferente para execuções diferentes, então a ordenação seria diferente. Por exemplo, se você usasseStringBuffer
em vez deString
2 .O seu verdadeiro erro aqui é um mal-entendido do que o javadoc está dizendo. Quando ele diz "esta classe não dá garantias quanto à ordem do mapa", não está dizendo que a ordem SERÁ inconsistente. O que está dizendo é que PODERIA ser inconsistente.
Observe que "poderia ser inconsistente" implica logicamente "poderia ser consistente".
Então o fato de você ter encontrado um cenário onde você (parece 3 ) obtém uma ordem consistente não é surpreendente. E não contradiz o javadoc!
1 - De fato, o algoritmo é o mesmo para todas as versões do Java.
2 - Isso é apenas para fins de demonstração: é a coisa errada a fazer por outros motivos.
3 - Seu teste não é suficiente para mostrar que sua plataforma fornecerá ordenações consistentes se, por exemplo, as chaves forem dinâmicas, ou houver um grande número de pares de valores-chave, ou as inserções forem feitas em uma sequência diferente.
Aqui estão algumas explicações sobre o que acontece nos bastidores.
Primeiro de tudo, mapas hash são uma estrutura de dados padrão de ciência da computação. Há variações, mas vale a pena dar uma olhada na Wikipedia e coisas assim.
A estrutura de dados HashMap (em Java) usa buckets de tamanho fixo (arrays) nos quais os itens são armazenados por um índice sendo a chave hash do item módulo o tamanho do array. Quando dois itens diferentes colidem obtendo o mesmo índice, um novo hash deve ser feito, um próximo índice é obtido. Isso significa que com o crescimento dos mapas hash, a reestruturação dos buckets pode acontecer. Portanto:
Você poderia continuar preenchendo um HashMap artificialmente até que a ordem dos itens comece a mudar. Depois de alguma adição, a ordem falhará.
Por exemplo (não testado):