De acordo com o seguinte documento de link: Java HashMap Implementation
Estou confuso com a implementação de HashMap
(ou melhor, um aprimoramento em HashMap
). Minhas dúvidas são:
primeiramente
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
Por que e como essas constantes são usadas? Eu quero alguns exemplos claros para isso. Como eles estão conseguindo um ganho de desempenho com isso?
Em segundo lugar
Se você HashMap
vir o código-fonte de no JDK, encontrará a seguinte classe interna estática:
static final class TreeNode<K, V> extends java.util.LinkedHashMap.Entry<K, V> {
HashMap.TreeNode<K, V> parent;
HashMap.TreeNode<K, V> left;
HashMap.TreeNode<K, V> right;
HashMap.TreeNode<K, V> prev;
boolean red;
TreeNode(int arg0, K arg1, V arg2, HashMap.Node<K, V> arg3) {
super(arg0, arg1, arg2, arg3);
}
final HashMap.TreeNode<K, V> root() {
HashMap.TreeNode arg0 = this;
while (true) {
HashMap.TreeNode arg1 = arg0.parent;
if (arg0.parent == null) {
return arg0;
}
arg0 = arg1;
}
}
//...
}
Como isso é usado? Eu só quero uma explicação do algoritmo .
fonte
String
, têm um espaço de valor muito maior do que oint
código hash, portanto, as colisões são inevitáveis. Agora, depende dos valores reais, comoString
s reais , que você coloca no mapa, se você obtém uma distribuição uniforme ou não. Uma má distribuição pode ser resultado apenas de má sorte.java.lang.String
tem uma função determinística e não criptográficahashCode
, para que os invasores possam criar strings distintas com hashCodes em conflito. Antes dessa otimização, isso poderia degradar as operações do HashMap para o tempo O (n), agora apenas degrada-as para O (log (n)).if the objects implement that interface, else the identity hash code.
eu estava procurando por esta outra parte.MIN_TREEIFY_CAPACITY
. Isso significa "Depois de inserir uma chave que deve ser hash para o balde que já contém 8 (TREEIFY_THRESHOLD
) chaves e se já houver 64 (MIN_TREEIFY_CAPACITY
) chavesHashMap
, a lista vinculada desse balde é convertida em árvore balanceada."Para ser mais simples (tanto quanto eu poderia mais simples) + mais alguns detalhes.
Essas propriedades dependem de muitas coisas internas que seriam muito interessantes de entender - antes de passar para elas diretamente.
TREEIFY_THRESHOLD -> quando um único balde atinge isso (e o número total excede
MIN_TREEIFY_CAPACITY
), ele é transformado em um nó de árvore vermelho / preto perfeitamente equilibrado . Por quê? Por causa da velocidade de pesquisa. Pense nisso de uma maneira diferente:Alguma introdução para o próximo tópico. Por que o número de caixas / baldes é sempre uma potência de dois ? Pelo menos dois motivos: mais rápido do que a operação do módulo e o módulo em números negativos será negativo. E você não pode colocar uma entrada em um balde "negativo":
Em vez disso, há um bom truque usado em vez do módulo:
Isso é semanticamente o mesmo que operação de módulo. Ele manterá os bits mais baixos. Isso tem uma consequência interessante quando você faz:
É aqui que a multiplicação dos baldes entra em jogo. Sob certas condições (levaria muito tempo para explicar com detalhes exatos ), os baldes dobram de tamanho. Por quê? Quando os baldes dobram de tamanho, há mais um bit entrando em ação .
Como tal, este processo é denominado re-hashing. Isso pode ficar lento. Isto é (para pessoas que se importam) porque HashMap é "brincado" como: rápido, rápido, rápido, lento . Existem outras implementações - pesquisar hashmap sem pausa ...
Agora UNTREEIFY_THRESHOLD entra em jogo após o re-hash. Nesse ponto, algumas entradas podem mover-se desses compartimentos para outros (eles adicionam mais um bit ao
(n-1)&hash
cálculo - e, como tal, podem mover-se para outros depósitos) e podem chegar a issoUNTREEIFY_THRESHOLD
. Neste ponto, não vale a pena manter a lixeira comored-black tree node
, mas emLinkedList
vez disso, comoMIN_TREEIFY_CAPACITY é o número mínimo de depósitos antes que um determinado depósito seja transformado em uma árvore.
fonte
TreeNode
é uma maneira alternativa de armazenar as entradas que pertencem a um único compartimento doHashMap
. Em implementações mais antigas, as entradas de um compartimento eram armazenadas em uma lista vinculada. No Java 8, se o número de entradas em um compartimento ultrapassar um limite (TREEIFY_THRESHOLD
), elas serão armazenadas em uma estrutura em árvore em vez da lista vinculada original. Esta é uma otimização.Desde a implementação:
fonte
TREEIFY_THRESHOLD
E o número total de caixas é pelo menosMIN_TREEIFY_CAPACITY
. Tentei cobrir isso em minha resposta ...Você precisaria visualizá-lo: digamos que haja uma chave de classe com apenas a função hashCode () substituída para retornar sempre o mesmo valor
e em outro lugar, estou inserindo 9 entradas em um HashMap com todas as chaves sendo instâncias desta classe. por exemplo
A travessia da árvore é mais rápida {O (log n)} do que LinkedList {O (n)} e, à medida que n cresce, a diferença se torna mais significativa.
fonte
compareTo
fromComparable
.identityHashCode
é outro mecanismo que usa.Key
não implementaComparable
,identityHashCode
será usado :)A mudança na implementação do HashMap foi adicionada ao JEP-180 . O objetivo era:
No entanto, o desempenho puro não é o único ganho. Isso também impedirá o ataque de HashDoS , no caso de um mapa hash ser usado para armazenar a entrada do usuário, porque a árvore vermelho e preto usada para armazenar dados no depósito tem o pior caso de complexidade de inserção em O (log n). A árvore é usada depois que um determinado critério é atendido - veja a resposta de Eugene .
fonte
Para entender a implementação interna do hashmap, você precisa entender o hashing. Hashing em sua forma mais simples, é uma maneira de atribuir um código único para qualquer variável / objeto após aplicar qualquer fórmula / algoritmo em suas propriedades.
Uma verdadeira função hash deve seguir esta regra -
“A função hash deve retornar o mesmo código hash toda vez que a função for aplicada a objetos iguais ou iguais. Em outras palavras, dois objetos iguais devem produzir o mesmo código hash de forma consistente. ”
fonte