Classes de baixo nível Java e análise de código fonte série-HashTable arquitetura de baixo nível e análise de código fonte

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 ; 
    }

 

Acho que você gosta

Origin www.cnblogs.com/starcrm/p/12678770.html
Recomendado
Clasificación