Coleção Detalhada - Coleção de Mapas

Interface do mapa

  • Mapa e coleção existem lado a lado. Usado para salvar dados com uma relação de mapeamento: valor-chave
  • Tanto a chave quanto o valor no mapa podem ser dados de qualquer tipo de referência
  • As chaves do Map são armazenadas em Set, e nenhuma repetição é permitida, ou seja, a classe correspondente ao mesmo objeto Map deve sobrescrever os métodos hashCode () e equals ()
  • Existe um relacionamento unilateral unilateral entre a chave e o valor, ou seja, uma classe de implementação comum única e definida da interface valueMap sempre pode ser encontrada por meio da chave especificada: HashMap, TreeMap, LinkedHashMap e Propriedades. Entre eles, HashMap é a classe de implementação com a interface de mapa mais usada

Métodos comuns da interface do mapa:

método Descrição
Objeto colocado (chave do objeto, valor do objeto) Adicione (ou modifique) o valor-chave especificado ao objeto de mapa atual
void putAll (Map m) Armazene todos os pares de valores-chave em m no mapa atual
Remover objeto (chave do objeto) Remova o par de valores-chave da chave especificada e retorne o valor
void clear () Limpar todos os dados do mapa atual
Object get (Object key) Obtenha o valor correspondente à chave especificada
boolean containsKey (chave do objeto) Se deve incluir a chave especificada
boolean containsValue (valor do objeto) Se deve incluir o valor especificado
tamanho interno () Retorna o número de pares de valores-chave no mapa
boolean isEmpty () Determine se o mapa atual está vazio
boolean equals (Object obj) Determine se o mapa atual e o objeto de parâmetro obj são iguais
Definir keySet () Retorna o conjunto de todas as chaves
Valores de coleção () Retorna a coleção composta de todos os valores
Definir entrySet () Retorna a coleção Set de todos os pares de valores-chave

Uma das classes de implementação do mapa: HashMap

  • HashMap é a classe de implementação com a interface de mapa mais usada.
  • Chaves nulas e valores nulos são permitidos. Como HashSet, a ordem de mapeamento não é garantida.
  • O conjunto composto por todas as chaves é definido: desordenado e não repetível. Portanto, a classe onde a chave está localizada deve ser reescrita: equals () e hashCode ()
  • A coleção composta por todos os valores é Coleção: desordenada e repetível. Portanto, a classe onde o valor está localizado deve ser reescrita: equals ()
  • Um valor-chave constitui uma entrada
  • O conjunto de todas as entradas é definido: desordenado e não repetível
  • O critério para o HashMap julgar que duas chaves são iguais é: as duas chaves retornam true por meio do método equals () e os valores de hashCode também são iguais.
  • O critério para o HashMap julgar que dois valores são iguais é: dois valores retornam verdadeiros por meio do método equals ().

Estrutura de armazenamento HashMap

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-40qcI5ou-1614868335362) (assets / image-20210217133622944.png)]

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-P0pjLdxg-1614868335367) (assets / image-20210217133639798.png)]

Constantes importantes no código-fonte HashMap

** DEFAULT_INITIAL_CAPACITY **: A capacidade padrão do HashMap, 16

** MAXIMUM_CAPACITY **: A capacidade máxima suportada de HashMap, 2 ^ 30

DEFAULT_LOAD_FACTOR : O fator de carga padrão do HashMap é 0,75

TREEIFY_THRESHOLD : o comprimento da lista vinculada no intervalo é maior do que o valor padrão, convertido em uma árvore vermelha e preta 8

UNTREEIFY_THRESHOLD : o nó armazenado na árvore vermelho-preto no intervalo é menor que o valor padrão e convertido em uma lista vinculada

MIN_TREEIFY_CAPACITY : A capacidade mínima da tabela de hash quando o nó no depósito é treed . (Quando o número de nós no intervalo é tão grande que precisa se tornar uma árvore vermelha e preta, se a capacidade da tabela de hash for menor que MIN_TREEIFY_CAPACITY, a operação de redimensionamento deve ser realizada neste momento. O valor de MIN_TREEIFY_CAPACITY é pelo menos 4 vezes mais TREEIFY_THRESHOLD.)

tabela : uma matriz de elementos de armazenamento, sempre 2 elevado a n

entrySet : armazena um conjunto de elementos específicos

tamanho : o número de pares de valores-chave armazenados no HashMap

modCount : O número de vezes que o HashMap foi expandido e a estrutura alterada.

limite : o valor crítico de expansão da capacidade, = capacidade * fator de preenchimento 16 * 0,75 = 12

loadFactor : fator de preenchimento

Análise do código-fonte antes de JDK1.8

Classe interna em HashMap: Node

static class Node<K,V> implements Map.Entry<K,V> {
    
    
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

    Node(int hash, K key, V value, Node<K,V> next) {
    
    
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    ...
}

A estrutura de armazenamento interno do HashMap é, na verdade, uma combinação de array e lista vinculada. Ao instanciar um HashMap, o sistema criará uma matriz de entrada com um comprimento de Capacidade. Esse comprimento é chamado de Capacidade na tabela de hash. O local onde os elementos podem ser armazenados nesta matriz é chamado de "depósito" (depósito), cada depósito tem seu próprio índice e o sistema pode localizar rapidamente os elementos no balde de acordo com o índice.

Cada bucket armazena um elemento, ou seja, um objeto Entry, mas cada objeto Entry pode carregar uma variável de referência para apontar para o próximo elemento, portanto, em um bucket, é possível gerar uma cadeia de Entry. E o elemento recém-adicionado é usado como o cabeçalho da lista vinculada.

O processo de adição de elementos:

Para adicionar entry1 (chave, valor) ao HashMap, você precisa primeiro calcular o valor hash da chave em entry1 (calculado de acordo com o hashCode () da classe onde a chave está localizada). Depois que o valor hash é processado, ele é obtido na matriz Entry [] subjacente. A localização a ser armazenada i. Se não houver nenhum elemento na posição i, a entrada1 será adicionada diretamente. Se a entrada2 já existir na posição i (ou se houver entrada3, entrada4 na lista encadeada), você precisará usar um método de loop para comparar sequencialmente a chave na entrada1 com outras entradas. Se os valores de hash forem diferentes uns dos outros, a adição direta será bem-sucedida. Se os
valores de hash forem diferentes, continue a comparar se os dois são iguais. Se o valor de retorno for verdadeiro, o valor da entrada1 é usado para substituir o valor da entrada cujo igual é verdadeiro. Se, depois de percorrê-lo novamente, for verificado que todos os retornos iguais são falsos, então a entrada1 ainda pode ser adicionada com sucesso. entry1 aponta para o elemento de entrada original.

Expansão de HashMap

Quando há mais e mais elementos no HashMap, a probabilidade de conflitos de hash torna-se cada vez maior, porque o comprimento da matriz é fixo. Portanto, a fim de melhorar a eficiência da consulta, é necessário expandir a matriz HashMap. Após a expansão da matriz HashMap, o ponto de maior consumo de desempenho aparece: os dados na matriz original devem ser recalculados em sua posição em a nova matriz e Go colocado, isso é resize.

Então, quando o HashMap se expandirá?

Quando o número de elementos no HashMap excede o tamanho da matriz (o comprimento total da matriz, não o tamanho do número na matriz) * loadFactor, a matriz será expandida. O valor padrão de loadFactor (DEFAULT_LOAD_FACTOR) é 0,75, que é um valor de compromisso. Ou seja, por padrão, o tamanho do array (DEFAULT_INITIAL_CAPACITY) é 16, então quando o número de elementos no HashMap excede 16 * 0,75 = 12 (este valor é o valor limite no código, também chamado de valor crítico), a matriz O tamanho de é expandido para 2 * 16 = 32, ou seja, dobrado e, em seguida, recalcula a posição de cada elemento na matriz, e esta é uma operação que consome muito desempenho, portanto, se já prevermos o número de elementos no HashMap, o número de elementos predefinidos pode melhorar efetivamente o desempenho do HashMap.

Análise do código-fonte após JDK 1.8

A estrutura de armazenamento interno do HashMap é, na verdade, uma combinação de array + lista vinculada + árvore. Ao instanciar um HashMap, initialCapacity e loadFactor serão inicializados. Ao colocar o primeiro par de relacionamentos de mapeamento, o sistema criará uma matriz de Node com um comprimento de initialCapacity. Esse comprimento é chamado de Capacidade na tabela de hash. O local onde os elementos podem estar armazenado na matriz é chamado de "intervalo". Cada intervalo tem seu próprio índice e o sistema pode encontrar rapidamente os elementos no intervalo com base no índice.

Cada bucket armazena um elemento, ou seja, um objeto Node, mas cada objeto Node pode carregar uma variável de referência próxima para apontar para o próximo elemento, portanto, em um bucket, é possível gerar uma cadeia Node. Também pode ser um objeto TreeNode.Cada objeto TreeNode pode ter dois nós folha à esquerda e à direita.Portanto, em um balde, é possível gerar uma árvore TreeNode. O elemento recém-adicionado serve como o último da lista vinculada ou o nó folha da árvore.

Então, quando o HashMap será expandido e terá a forma de árvore?

Quando o número de elementos no HashMap excede o tamanho da matriz (o comprimento total da matriz, não o tamanho do número na matriz) * loadFactor, a matriz será expandida. O valor padrão de loadFactor (DEFAULT_LOAD_FACTOR) é 0,75, que é um valor de compromisso. Em outras palavras, por padrão, o tamanho do array (DEFAULT_INITIAL_CAPACITY) é 16, então quando o número de elementos no HashMap exceder 16 * 0,75 = 12 (este valor é o valor limite no código, também chamado de valor crítico), o array O tamanho de é expandido para 2 * 16 = 32, ou seja, dobrado e, em seguida, recalcula a posição de cada elemento no array, e esta é uma operação que consome muito desempenho, portanto, se previmos o número de elementos em o HashMap, então O número de elementos predefinidos pode melhorar efetivamente o desempenho do HashMap.

Quando o número de objetos em uma das cadeias no HashMap atingir 8, se a capacidade não chegar a 64, o HashMap será expandido primeiro. Se atingir 64, a cadeia se tornará uma árvore, e o tipo de nó será alterado de Node para o tipo TreeNode. Obviamente, se depois que o relacionamento de mapeamento for removido, da próxima vez que o método de redimensionamento determinar que o número de nós na árvore é menor que 6, ele também converterá a árvore em uma lista vinculada.

A chave da relação de mapeamento pode ser modificada? Resposta: Não modifique a
relação de mapeamento e armazene o valor de hash da chave no HashMap, de modo que você não precise recalcular o valor de hash de cada Entrada ou Nó (TreeNode) toda vez que você procurar, se já tiver coloque o relacionamento de mapeamento no Mapa, e então modifique o atributo chave, e este atributo participa do cálculo do valor do hashcode, então ele fará com que a correspondência falhe.

Resumo: JDK 1.8 em comparação com as mudanças anteriores:

  1. HashMap map = new HashMap (); // Por padrão, uma matriz de comprimento 16 não é criada primeiro
  2. Quando map.put () é chamado pela primeira vez, uma matriz de comprimento 16 é criada
  3. A matriz é do tipo Node, chamada de tipo de entrada em jdk7
  4. Ao formar uma estrutura de lista vinculada, o par de valores-chave recém-adicionado está no final da lista vinculada (sete para cima e oito para baixo)
  5. Quando o comprimento da lista encadeada na posição de índice especificada da matriz é> 8 e o comprimento da matriz no mapa é> 64, todos os pares de valores-chave nesta posição de índice são armazenados em uma árvore vermelho-preto.

Comparado com jdk7, jdk8 é diferente na implementação subjacente:

  • new HashMap (): A camada inferior não criou uma matriz de comprimento 16
  • A matriz subjacente do jdk 8 é: Node [], não Entry []
  • Quando o método put () é chamado pela primeira vez, a camada inferior cria uma matriz de comprimento 16
  • A estrutura subjacente do jdk7 tem apenas: array + lista vinculada. A estrutura subjacente em jdk8: array + lista vinculada + árvore vermelho-preto.
  • Ao formar uma lista vinculada, sete altos e baixos (jdk7: novos elementos apontam para elementos antigos. Jdk8: elementos antigos apontam para novos elementos)
  • Quando o número de dados em uma posição de índice da matriz na forma de uma lista encadeada> 8 e o comprimento da matriz atual> 64, os dados nesta posição de índice são alterados para usar o armazenamento em árvore vermelho-preto.

Questões de entrevista:

Conte-me sobre seu conhecimento de métodos put / get em HashMap. Se você entendeu, fale sobre o mecanismo de expansão do HashMap? Qual é o tamanho padrão? Qual é o fator de carga (ou taxa de preenchimento)? Qual é o limite de taxa de transferência (ou limite, limite)?

Qual é o impacto do valor do fator de carga no HashMap?

  • O tamanho do fator de carga determina a densidade de dados do HashMap.
  • Quanto maior o fator de carga, maior a densidade, maior a probabilidade de colisões e mais longa a lista vinculada na matriz. Isso aumentará o número de comparações durante a consulta ou inserção e reduzirá o desempenho.
  • Quanto menor o fator de carga, mais fácil é acionar a expansão. Quanto menor a densidade dos dados, menor a probabilidade de colisão, menor a lista vinculada na matriz, menor o número de comparações entre consultas e inserções e maior o desempenho. Mas uma certa quantidade de espaço de conteúdo será desperdiçada. Além disso, a expansão frequente também afetará o desempenho. Recomenda-se inicializar um espaço maior.
  • De acordo com a referência e a experiência de pesquisa em outros idiomas, o fator de carga será considerado como sendo 0,7 ~ 0,75, neste momento o comprimento médio da pesquisa está próximo de uma constante.

Mapeie a categoria dois de implementação: LinkedHashMap

  • LinkedHashMap é uma subclasse de HashMap
  • Com base na estrutura de armazenamento do HashMap, um par de listas duplamente vinculadas é usado para registrar a ordem de adição de elementos
  • Semelhante ao LinkedHashSet, LinkedHashMap pode manter a iteração do Map
  • Pedido: o pedido de iteração é consistente com o pedido de inserção do par de valor-chave

Classe interna em LinkedHashMap: Entry

static class Entry<K,V> extends HashMap.Node<K,V> {
    
    
    Entry<K,V> before, after;//能够记录添加的元素的先后顺序
    Entry(int hash, K key, V value, Node<K,V> next) {
    
    
        super(hash, key, value, next);
    }
}

Categoria de implementação de mapa três: TreeMap

  • Quando TreeMap armazena pares de valor-chave, ele precisa ser classificado de acordo com os pares de valor-chave. TreeMap pode garantir que todos os pares de valores-chave estejam em um estado ordenado.
  • A camada inferior do TreeSet usa uma estrutura de árvore vermelha e preta para armazenar dados
  • Classificação da chave do TreeMap:
    • Ordenação natural: todas as chaves de TreeMap devem implementar a interface Comparable e todas as chaves devem ser objetos da mesma classe, caso contrário, ClasssCastException será lançada
    • Classificação personalizada: Ao criar um TreeMap, passe um objeto Comparator, que é responsável por classificar todas as chaves no TreeMap. No momento, não há necessidade de Map Key para implementar a interface Comparable
  • O critério para TreeMap julgar que duas chaves são iguais: duas chaves
    retornam 0 por meio do método compareTo () ou compare ().

Mapeie a categoria quatro de implementação: Hashtable

  • Hashtable é uma classe de implementação de mapa antiga, fornecida pelo JDK1.0. Ao contrário do HashMap, o Hashtable é seguro para threads
  • O princípio de implementação de Hashtable é o mesmo de HashMap e as funções são as mesmas. A estrutura da tabela de hash é usada na camada inferior e a velocidade de consulta é rápida. Em muitos casos, ela pode ser usada mutuamente
  • Ao contrário do HashMap, o Hashtable não permite o uso de nulo como chave e valor
  • Como o HashMap, o Hashtable não pode garantir a sequência de pares de valor-chave.
  • O critério para Hashtable para julgar que duas chaves são iguais e dois valores são iguais é consistente com HashMap

Mapeie a categoria cinco de implementação: Propriedades

  • A classe Properties é uma subclasse de Hashtable, este objeto é usado para processar arquivos de propriedades
  • Uma vez que a chave e o valor no arquivo de propriedades são tipos de string, a chave e o valor em Propriedades são ambos os tipos de string
  • Ao acessar os dados, é recomendável usar o método setProperty (String key, String value) e o método getProperty (String key)
Properties pros = new Properties();
pros.load(new FileInputStream("jdbc.properties"));
String user = pros.getProperty("user");
System.out.println(user);

Acho que você gosta

Origin blog.csdn.net/qq_44346427/article/details/110729397
Recomendado
Clasificación