Alguns pontos
- A estrutura de dados subjacente do HashTable é baseada em uma matriz de listas vinculadas (O (n));
- HashTable não permite chave vazia e valor vazio;
- Os elementos HashMap não são classificados de acordo com a ordem em que são gravados, mas são classificados de acordo com o hash da chave, usando o módulo n (os métodos de otimização de algoritmo (n-1) e hash)
- O HashTable é uma classe segura para threads, mas usa sincronização direta no método, que usa bloqueios de bloqueios internos (bloqueando toda a tabela) para garantir a segurança do thread e sua eficiência de concorrência é baixa. Considere o pacote Concurrent, como ConcurrentHashMap;
- O Hashtable, como o HashMap, possui dois parâmetros que afetam o desempenho, a capacidade inicial e o fator de carga, e o fator de carga também é 0,75 por padrão;
- A capacidade inicial initialCapacity é 11, não é necessário que seja um múltiplo exponencial de 2, e não 16 do HashMap;
- O algoritmo de hash usa diretamente o código de hash do objeto (o código de hash da chave), sem a otimização do HashMap (XOR alto ou baixo de 16 bits);
- O Hashtable cria uma matriz durante a inicialização, o HashMap é um carregamento lento;
- O algoritmo de posicionamento é (e.hash & 0x7FFFFFFF)% tab.length. Como o número de buckets no HashMap deve ser um múltiplo exponencial de 2, o método para obter o índice do bucket pode ser otimizado como hash & (length-1);
- A eficiência de inserção do Hashtable é ineficiente. Cada inserção deve percorrer a lista vinculada uma vez. Cada vez que é O (n), a eficiência é menor que o HashMap. O HashMap geralmente é O (1) para posicionamento direto. LogN), O (n) de um pequeno número de elementos na lista vinculada, a lista vinculada é menor que 8;
- A eficiência de leitura do Hashtable também é baixa, cada leitura deve percorrer a lista vinculada uma vez, cada vez que é O (n), o HashMap é quase O (1), localiza diretamente h & (comprimento-1), mais vermelho e preto O (LogN), então é quase O (1);
Definição de classe
classe pública Hashtable <K, V> estende o Dictionary <K, V> implementa Map <K, V>, Cloneable, java.io.Serializable
Atributos
- Tabela de entrada <?,?> []: Matriz de tipo de entrada, usada para armazenar pares de valores-chave no Hashtable;
- int count: quantos pares de valores-chave estão armazenados na hashtable
- int threshold: quando o valor da contagem é maior que esse valor, a tabela de hash expande a capacidade e rehash ()
- float loadFactor: threshold = tamanho inicial da tabela de hash * loadFactor, capacidade inicial padrão de 11, loadFactor padrão de 0,75
- int modCount: o número de vezes que essa estrutura HashTable foi modificada.Este valor é incrementado toda vez que um elemento é adicionado, atualizado ou excluído. Implemente o mecanismo "fail-fast". Ao iterar no Hashtable em uma coleção simultânea, se outros encadeamentos fizerem modificações estruturais no Hashtable, o iterador comparará o esperadoModCount e modCount para ver se eles são consistentes. Se eles não forem consistentes, uma ConcurrentModificationException será lançada.
Construtor
Hashtable pública ( int initialCapacity, float loadFactor) { if (initialCapacity <0 ) lança nova IllegalArgumentException ("Capacidade ilegal:" + initialCapacity); if (loadFactor <= 0 || Float.isNaN (loadFactor)) lança nova IllegalArgumentException ("Carga ilegal:" + loadFactor); if (initialCapacity == 0 ) initialCapacity = 1 ; this .loadFactor = loadFactor; tabela =nova entrada <?,?> [initialCapacity]; limite = ( int ) Math.min (capacidade inicial * loadFactor, MAX_ARRAY_SIZE + 1 ); } Hashtable público ( int initialCapacity) { this (initialCapacity, 0.75f ); } public Hashtable () { this (11, 0,75f ); } Hashtable pública (Mapa <? estende K,? estende V> t) { this (Math.max (2 * t.size (), 11), 0,75f ); putAll (t); }
consistência modCount
public static void main (String [] args) { Hashtable <Número inteiro, String> tb = new Hashtable <Número inteiro, String> (); tb.put ( 1, "BUPT" ); tb.put ( 2, "PKU" ); tb.put ( 3, "THU" ); Iterador <Entrada <Inteiro, String >> iter = tb.entrySet (). Iterator (); while (iter.hasNext ()) { Entrada <?,?> entry = (Entrada <?,?>) iter.next (); // 此处会抛出异常 System.out.printlnl (entry.getValue ()); if (".equals (entry.getValue ())) { tb.remove (entry.getKey ()); } } } / * 输出 :: THU Exceção no encadeamento "main" java.util.ConcurrentModificationException em java.util.Hashtable $ Enumerator.next (Hashtable.java:1367) em ali.Main.main (Main.java:16 ) * /
Exceção ConcurrentModificationException, o valor de modCount é atualizado toda vez que os dados na hashtable são modificados e o próximo método do iterador Enumerador <T> julgará modCount! = ExpectedModCount
public T next () { // Primeiro determine se modCount e o esperadoModCount são iguais // Como o objeto Hashtable modifica o valor de modCount através do método tb.remove () no programa principal, o expectModCount e o modCount não são iguais e uma exceção é lançada // resolvida A maneira é substituir o método tb.remove () pelo método iter.remove () se (modCount! = ExpectedModCount ) lançar nova ConcurrentModificationException (); return nextElement (); }
// Este método modifica modCount e expectModCount enquanto remove o elemento O valor do public void remove () { if (! Iterator) lança novosUnsupportedOperationException (); if (lastReturned == null ) lança novo IllegalStateException ("Hashtable Enumerator" ); if (modCount! =pectedModCount ) lança novo ConcurrentModificationException (); sincronizado (Hashtable. presente ) { entrada <?,?> [] tab = Hashtable. esta tabela; int index = (lastReturned.hash & 0x7FFFFFFF)% tab.length; @SuppressWarnings ( entrada "desmarcada" )<K, V> e = (entrada <K, V> ) guia [index]; para (Entrada <K, V> anterior = nulo ; e! = nulo ; anterior = e, e = e.próximo) { if (e == lastReturned) { modCount ++ ; expectModCount ++ ; if (anterior == nulo ) guia [index] = e.next; else prev.next = e.next; contagem ;- lastReturned = nulo ; retorno ; } } lançar novo ConcurrentModificationException (); } }
Método principal
colocar
Pode-se observar que cada inserção deve percorrer a lista vinculada uma vez, cada vez que é O (n), a eficiência é menor que o HashMap, o HashMap geralmente é O (LogN) na árvore vermelho-preta, O (n) na lista vinculada, posicionamento direto É 1
público a sincronizado V PUT (Chave K, valor V) { // valor não permite nulo IF (valor == nulo ) { throw new new um NullPointerException (); } // Faz A chave não é certeza que o Hashtable já. Entrada <? ?,> Tab [] = Tabela; // obter a chave de hash int de hash = key.hashCode (); // obter o índice correspondente ao balde de hash na matriz int índice = (Hash & 0x7FFFFFFF)% tab.length; @SuppressWarnings ( "desmarcado" ) // Coloque o cabeçalho da lista vinculada no bucket Entrada <K, V> entry = (entrada <K, V> ) guia [index]; // Atravessando do início para (; entry! = Null ; entry = entry.next) { // Quando os valores de hash forem iguais e as chaves forem iguais , Substitua o valor antigo se ((entry.hash == hash) && entry.key.equals (key)) { V old = entry.value; entry.value = value; return old; } } // se a mesma chave não for encontrada Em seguida, adicione um novo nó addEntry (hash, chave, valor, índice); return null ; }
addEntry
private void addEntry ( hash int , chave K, valor V, índice int ) { modCount ++ ; Entrada <?,?> Tab [] = tabela; // rehash if (count> = threshold) { // Realiza novamente a tabela se o limite for excedido rehash (); tab = tabela; hash = chave.hashCode (); índice = (hash & 0x7FFFFFFF)% tab.length; } // Cria a nova entrada. @SuppressWarnings ("não marcado" ) Entrada <K, V> e = (entrada <K, V> ) guia [index]; tab [index] = nova entrada <> (hash, chave, valor, e); count ++ ; }
obter
public get sincronizado V get (chave do objeto) { entrada <?,?> tab [] = tabela; int hash = key.hashCode (); int index = (hash & 0x7FFFFFFF)% tab.length; // atravessar todos os elementos, ha Se o valor for o mesmo da chave, retorne para (Entrada <?,?> E = tab [index]; e! = Nulo ; e = e.next) { if ((e.hash == hash) && e.key. igual a (chave)) { return (V) e.value; } } return null ; }