A maioria de vocês concordará que
Você pode ler mais sobre isso aqui: Trabalhando com hashCode e método equals em Java
HashMap
, hoje, é o tópico preferido para discussão durante as entrevistas. Às vezes, tive discussões semelhantes com meus colegas e isso realmente ajudou. Agora terei essa discussão com você. Presumo que se você estiver interessado nos detalhes internos e no funcionamento do HashMap, então já está familiarizado com os fundamentos do HashMap , então pularei essa parte. Mas se você é novo nisso, sugiro que acesse o site Java Docs . Antes de prosseguirmos, recomendo fortemente que você leia meu artigo anterior: Trabalhando com hashCode e o método equals em Java. Conteúdo deste artigo:
- A única resposta possível.
- O que é hash.
- Um pouco sobre a aula
Entry
. - O que faz o
put()
. - Como funciona o método
get()
. - Notas
A única resposta possível
Se alguém me pedir para explicar " Como funciona o HashMap?" ", responderei simplesmente: " De acordo com os princípios do Hashing ." Não poderia ser mais simples. Para entender isso e obter uma resposta extensa, você precisa ter certeza de que conhece os fundamentos do Hashing. Certo?O que é hash
Hashing em sua forma mais simples é uma forma de converter qualquer variável/objeto em um código único após aplicar qualquer fórmula/algoritmo às suas propriedades. Uma função hash verdadeira deve seguir a seguinte regra: Uma função hash deve retornar o mesmo código hash sempre que for aplicada a objetos iguais ou iguais. Em outras palavras, dois objetos idênticos devem retornar os mesmos códigos hash por sua vez.Nota: Todos os objetos em java herdam a implementação padrão hashCode() da função descrita na classe Object . Esta função retorna um código hash obtido pela conversão do endereço interno de um objeto em um número, o que leva à criação de um código único para cada objeto individual. |
Um pouco sobre a aula Entry
Por definição, um mapa é “um objeto que armazena valores e chaves em pares”. Muito simples, certo? Então, deve haver algum tipo de mecanismo no HashMap que armazene pares de Valores e Chaves? Resposta - Sim.HashMap
tem uma classe interna Entry
parecida com esta:
static class Entry implements Map.Entry
{
final K key;
V value;
Entry next;
final int hash;
...//остальной code тут…
}
Naturalmente, a classe Entry
possui uma chave e um valor armazenados como atributos. A chave está marcada como final
e também vemos dois campos adicionais: next
e hash
. Tentaremos entender a finalidade desses campos à medida que o artigo avança.
O que o método Java put() faz?
Antes de mergulharmos na implementação do métodoput()
, é muito importante entender que as instâncias de uma classe Entry
são armazenadas em um array. A classe HashMap define esta variável como:
/**
* Размер таблицы, изменяется при необходимости. Длина всегда должна быть
* кратна двум!
*/
transient Entry[] table;
Agora dê uma olhada no código de implementação do método put()
:
/**
* Связывает определенное meaning с определенным ключом в этой карте(map).
* Если карта перед этим содержала meaning для данного ключа, это meaning
* заменится на новое.
*
* @param key
* ключ с которым указанное meaning должно быть связано.
* @param value
* meaning которое должно быть связано с ключом.
* @return вернет предыдущее meaning связанное с key, or null
* если не было значений связанных с key. (Вернет null
* так же, если перед этим key был связан со meaningм null)
*/
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<k , V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
Vamos descobrir passo a passo:
- Em primeiro lugar, verificamos se a chave existe. Se a chave não existir (
null
), o valor será colocado na tabela na posição zero porque o código hash do valor énull
,это – всегда 0
. - Na próxima etapa, o valor hash é calculado usando o código hash da chave obtida pela chamada do método
hashCode()
. Este valor hash é usado para calcular a posição no array onde o objeto será colocadoEntry
. Os projetistas do JDK presumiram que uma função mal escritahashCode()
poderia retornar um valor de hash muito alto ou muito baixo. Para resolver esse problema, eles introduziram outrahash()
função e passaram o valor hash de um objeto para ela para fazer com que o valor hash correspondesse ao tamanho do array. - Agora a função é chamada
indexFor(hash, table.length)
para calcular a posição exata onde o objeto será colocadoEntry
. - É aqui que começa a parte principal. Agora, com base no que sabemos que - dois objetos desiguais podem ter códigos hash iguais, fazemos a pergunta: Dois objetos diferentes serão colocados na mesma posição no array [bucket]
LinkedList
? Se você se lembra, a classeEntry
possui um atributo "next
". Este atributo sempre aponta para o próximo objeto da cadeia. Este é exatamente o comportamentoLinkedList
.
Entry
são armazenados no formato LinkedList
. Quando um objeto Entry
deve ser colocado em um local específico, o HashMap verifica se já existe uma entrada nesse local. Se não houver entrada, o objeto será colocado nesta posição. Se, entretanto, já existir um objeto nesta posição, o próximo atributo será verificado. Se ele retornar null
e o objeto atual Entry
se tornar o próximo link no arquivo LinkedList
. Se a próxima variável não for null
, o procedimento é repetido para a próxima até encontrar null
. E se colocarmos outro objeto com valor diferente, mas com a mesma chave de antes? Logicamente, isso deve resultar na substituição do valor antigo. Como isso acontece? Em geral, após determinar a posição de um objeto Entry
, ao percorrer LinkedList
até a posição calculada, HashMap
ele chama o método de comparação chave para cada objeto Entry
. Todos esses Entry
objetos LinkedList
podem ter códigos hash semelhantes, mas o método equals()
verificará a verdadeira semelhança. Isso substituirá apenas o valor dentro do arquivo Entry
. Assim, o HashMap garante a exclusividade de todas as chaves.
Como funciona o método Java get()?
Agora temos uma ideia de como os pares de valores-chave são armazenados em arquivosHashMap
. A próxima grande questão é: O que acontece quando um objeto é passado de um HashMap para um método get()
? Como é determinado o valor de um objeto? Já deveríamos saber a resposta, pois a forma como a unicidade de uma chave é determinada no método put()
tem a mesma lógica que o método aplica get()
. Depois de HashMap
determinar a chave do objeto passado como argumento, ele simplesmente retorna o valor do arquivo Entry
. Se nenhuma correspondência for encontrada, o método get()
retornará null
. Vamos dar uma olhada no código:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<k,V>e=table[indexFor(hash,table.length)];e!=null;e=e.next){
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
O código acima é semelhante ao método put()
até agora if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
, depois disso ele simplesmente retorna o valor do objeto.
Notas
- A estrutura de dados a ser armazenada em um objeto
Entry
é um array com um nometable
e um tipoEntry
. - Cada posição individual na matriz é chamada de bucket porque pode conter o primeiro elemento
LinkedList
dos objetosEntry
. hashCode()
A chave é necessária para calcular a posição do objetoEntry
.equals()
A chave é usada para verificar a exclusividade da chave no mapa (map
).hashCode()
eequals()
Valores não são usados em métodosget()
eset()
emHashMap
.- O código hash para chaves com valor
null
é sempre 0. E tal objetoEntry
sempre será armazenado na posição zero do array.
GO TO FULL VERSION