AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79218335
Accepted
happy
happy
Asked: 2024-11-24 00:31:40 +0800 CST2024-11-24 00:31:40 +0800 CST 2024-11-24 00:31:40 +0800 CST

Ordem de iteração do Java HashMap - o comportamento parece consistente apesar da documentação declarar o contrário [duplicado]

  • 772
Esta pergunta já tem respostas aqui :
a ordem de iteração do Java HashMap keySet() é consistente? (12 respostas)
A ordem dos valores recuperados de um HashMap é a ordem de inserção (7 respostas)
A ordem dos elementos do HashMap é reproduzível? (3 respostas)
A ordem do mapa/coleção é estável entre as chamadas? (5 respostas)
Fechado há 10 horas .

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?

java
  • 4 4 respostas
  • 141 Views

4 respostas

  • Voted
  1. Best Answer
    Basil Bourque
    2024-11-24T02:19:33+08:002024-11-24T02:19:33+08:00

    O comportamento pode variar

    tentando entender o comportamento real do HashMap

    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 comportamento pode variar em tempo de execução . Os programadores de implementação podem ter escrito o comportamento para mudar dependendo da quantidade de elementos, ou seus tipos, ou seu conteúdo. A ordem de iteração pode até mudar cada vez que você faz um loop pelo mapa.
    • O comportamento pode variar com qualquer nova versão . Os programadores são livres para alterar sua implementação a qualquer momento, desde que cumpram o contrato prometido no Javadoc.

    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, e TreeMap. 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+) e SortedMap(Java 2+), ambas implementadas por duas das três classes nomeadas acima: ConcurrentSkipListMape TreeMap.

    Considere usar TreeMapprimeiro, se thread-safety não for uma preocupação. Grandes quantidades de dados podem ter melhor desempenho com ConcurrentSkipListMap.

    Diagrama da hierarquia de classes Map em Java 21+, por Basil Bourque, 2024-11

    Codificação de sereia:

    ---
    title: Java Map hierarchy
    ---
    classDiagram
        Map <|-- SequencedMap
        SequencedMap <|-- SortedMap
        SortedMap <|-- NavigableMap
        NavigableMap <|-- ConcurrentNavigableMap
        NavigableMap <|.. TreeMap : implements
        ConcurrentNavigableMap <|.. ConcurrentSkipListMap : implements
        SequencedMap <|.. LinkedHashMap : implements
        class Map { <<interface>> }
        class SequencedMap { <<interface>> }
        class NavigableMap { <<interface>> }
        class SortedMap { <<interface>> }
        class ConcurrentNavigableMap { <<interface>> }
        class TreeMap { }
        class ConcurrentSkipListMap { }
        class LinkedHashMap { }
    

    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 .

    • 7
  2. Stephen C
    2024-11-24T08:37:46+08:002024-11-24T08:37:46+08:00

    Isso é explicável. Primeiro, alguns fatos relevantes:

    1. Você está usando um tipo de chave ( String) que tem um hashCodemétodo cujo algoritmo é especificado e que tem a garantia de fornecer o mesmo valor de hashcode para todas as execuções 1 .

    2. Você está inserindo os mesmos pares de chave/valor exatamente na mesma ordem todas as vezes.

    Dado o acima, a implementação do HashMapque 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 HashMapimplementaçã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 hashCodepudesse ser diferente para execuções diferentes, então a ordenação seria diferente. Por exemplo, se você usasse StringBufferem vez de String2 .


    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.

    • 3
  3. grimur82
    2024-11-24T07:41:50+08:002024-11-24T07:41:50+08:00

    Aqui estão algumas explicações sobre o que acontece nos bastidores.

    1. A inserção de elementos em um HashMap não depende da ordem em que são adicionados. Em vez disso, depende do código hash da chave e da disponibilidade de slots no array interno do mapa.
    2. Se o slot computado para uma chave já estiver ocupado (devido a uma colisão de hash), o HashMap resolve isso por encadeamento ou sondagem. Esse processo de resolução não preserva a ordem de inserção e pode envolver atravessar ou modificar vários slots.
    3. Quando a capacidade do mapa é excedida, um novo mapa maior é criado, e todas as entradas existentes são rehashadas e redistribuídas. Esse processo de rehash ignora completamente a ordem de inserção original.
    • 1
  4. Joop Eggen
    2024-11-24T08:49:02+08:002024-11-24T08:49:02+08:00

    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:

    Com o tempo o pedido não é garantido.

    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):

    List<String> order = new ArrayList<>();
    Map<String, String> map = new HashMap<>();
    for (;;) {
        key = ... read from dictionary 
        String oldKey = map.put(key, value):
        if (oldKey == null) {
            // Time to check the order:
            List<String> nextOrder = new ArrayList<>(map.keySet());
            for (int i = 0; i < nextOrder.size(); ++i) {
                if (i == order.size() || !order.get(i).equals(nextOrder.get(i)) {
                    if (nextOrder.get(i).equals(key)) {
                        orders.add(i, key);
                    } else {
                        System.out.println("Order changed");
                        return;
                    }
                }
            }
        }
     }
    
    • 1

relate perguntas

  • Lock Condition.notify está lançando java.lang.IllegalMonitorStateException

  • Resposta de microsserviço Muitos para Um não aparece no carteiro

  • Validação personalizada do SpringBoot Bean

  • Os soquetes Java são FIFO?

  • Por que não é possível / desencorajado definir um lado do servidor de tempo limite de solicitação?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve