Clases de bajo nivel de Java y serie de análisis de código fuente: arquitectura de bajo nivel de HashTable y análisis de código fuente

Algunos puntos

  • La estructura de datos subyacente de HashTable se basa en una matriz de listas vinculadas (O (n));
  • HashTable no permite clave vacía y valor vacío;
  • Los elementos HashMap no se ordenan según el orden en que se escriben, sino que se ordenan según el hash de la clave, tomando n módulo (la optimización del algoritmo usa (n-1) y hash)
  • HashTable es una clase segura para subprocesos, pero utiliza sincronizado directamente en el método, que utiliza bloqueos internos de bloqueo (bloqueo de toda la tabla) para garantizar la seguridad del subproceso, y su eficiencia de concurrencia es baja. Considere el paquete concurrente, como ConcurrentHashMap;
  • Hashtable, como HashMap, tiene dos parámetros que afectan el rendimiento, la capacidad inicial y el factor de carga, y el factor de carga también es 0.75 por defecto;
  • La capacidad inicial initialCapacity es 11, no es necesario que sea un múltiplo exponencial de 2, no 16 de HashMap;
  • El algoritmo hash usa directamente el código hash del objeto (el código hash de la clave), sin la optimización de HashMap (XOR alto o bajo de 16 bits);
  • Hashtable crea una matriz durante la inicialización, HashMap es carga lenta;
  • El algoritmo de posicionamiento es (e.hash & 0x7FFFFFFF)% tab.length. Dado que el número de depósitos en el HashMap debe ser un múltiplo exponencial de 2, el método para obtener el índice del depósito se puede optimizar como hash & (length-1);
  • La eficiencia de inserción de Hashtable es ineficiente. Cada inserción tiene que atravesar la lista vinculada una vez. Cada vez es O (n), la eficiencia es menor que HashMap. HashMap es generalmente O (1) para el posicionamiento directo. LogN), O (n) de un pequeño número de elementos en la lista vinculada, la lista vinculada es inferior a 8;
  • La eficiencia de lectura de Hashtable también es baja, cada lectura debe atravesar la lista vinculada una vez, cada vez es O (n), HashMap es casi O (1), ubica directamente h & (longitud-1), más el árbol rojo-negro O (LogN), por lo que es casi O (1);

Definición de clase

La  clase pública Hashtable <K, V>
     extiende Diccionario <K, V>
     implementa Map <K, V>, Cloneable, java.io. Serializable

Atributos

  • Entrada <?,?> [] Tabla: matriz de tipo de entrada, utilizada para almacenar pares clave-valor en Hashtable;
  • int count: cuántos pares clave-valor se almacenan en la tabla hash
  • umbral de int: cuando el valor de conteo es mayor que este valor, la tabla hash expande la capacidad y rehash ()
  • flotante loadFactor: umbral = tamaño inicial de la tabla hash * loadFactor, la capacidad inicial predeterminada es 11, loadFactor predeterminado es 0,75
  • int modCount: la cantidad de veces que se modificó esta estructura HashTable. Este valor se incrementa cada vez que se agrega, actualiza o elimina un elemento. Implemente el mecanismo de "falla rápida". Al iterar en el Hashtable en una colección concurrente, si otros subprocesos realizan modificaciones estructurales en el Hashtable, el iterador comparará el esperadoModCount y modCount para ver si son consistentes. Si no son consistentes, se lanzará una ConcurrentModificationException.

Constructor

  public Hashtable ( int initialCapacity, float loadFactor) {
         if (initialCapacity <0 )
             arroja una  nueva IllegalArgumentException ("Illegal Capacity:" + 
                                               initialCapacity); 
        if (loadFactor <= 0 || Float.isNaN (loadFactor))
             arroja una  nueva IllegalArgumentException ("Illegal Load:" + loadFactor); 

        if (initialCapacity == 0 ) 
            initialCapacity = 1 ;
        this .loadFactor = loadFactor; 
        tabla =nueva entrada <?,?> [initialCapacity]; 
        umbral = ( int ) Math.min (initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1 ); 
    } 

    public Hashtable ( int initialCapacity) {
         this (initialCapacity, 0.75f ); 
    } 

    public Hashtable () {
         this (11, 0.75f ); 
    } 

    public Hashtable (Mapa <? extiende K ,? extiende V> t) {
         this (Math.max (2 * t.size (), 11), 0.75f ); 
        putAll (t); 
    }

consistencia modCount

public  static  void main (String [] args) { 
         Hashtable <Integer, String> tb = new Hashtable <Integer, String> (); 
         tb.put ( 1, "BUPT" ); 
         tb.put ( 2, "PKU" ); 
         tb.put ( 3, "THU" ); 
         Iterator <Entry <Integer, String >> iter = tb.entrySet (). Iterator ();
         while (iter.hasNext ()) { 
             Entry <?,?> entry = (Entry <?,?>) iter.next (); // 此处 会 抛出 异常
             System.out.println (entry.getValue ());
             si (".equals (entry.getValue ())) { 
                 tb.remove (entry.getKey ()); 
             } 
         } 
    } 
/ * 输出 结果 如下 :
THU 
Excepción en el hilo "main" java.util.ConcurrentModificationException 
    en java.util.Hashtable $ Enumerator.next (Hashtable.java:1367) 
    en ali.Main.main (Main.java:16 ) * /

Con excepción de ConcurrentModificationException, el valor de modCount se actualiza cada vez que se modifican los datos en la tabla hash, y el siguiente método del iterador Enumerator <T> juzgará modCount! = ExpectedModCount

     public T next () {
             // Primero determina si modCount y esperabaModCount son iguales
             // Debido a que el objeto Hashtable modifica el valor de modCount a través del método tb.remove () en el programa principal, el esperadoModCount y modCount no son iguales y se lanza una excepción
             // se resuelve La forma es reemplazar el método tb.remove () con el método iter.remove () 
            si (modCount! = ExpectedModCount )
                 arroja una  nueva ConcurrentModificationException ();
             return nextElement (); 
        } 
// Este método modifica modCount y expectModCount mientras elimina el elemento El valor de public void remove () { if (! Iterator) throw newUnsupportedOperationException (); if (lastReturned == null ) arroja una nueva IllegalStateException ("Hashtable Enumerator" ); if (modCount! =pectedModCount ) lanza una nueva ConcurrentModificationException (); sincronizado (Hashtable. this ) { Entry <?,?> [] tab = Hashtable. esta tabla; int index = (lastReturned.hash & 0x7FFFFFFF)% tab.length; @SuppressWarnings ( "sin marcar " ) Entrada<K, V> e = (Entrada <K, V> ) pestaña [índice]; para (Entrada <K, V> anterior = nulo ; e! = nulo ; anterior = e, e = e.siguiente) { if (e == lastReturned) { modCount ++ ; pectedModCount ++ ; pestaña if (anterior == nulo ) [index] = e.next; más prev.next = e.next; contar - ; lastReturned = null ; volver ; } } lanzar nueva ConcurrentModificationException (); } }

Método principal

poner

Se puede ver que cada inserción debe atravesar la lista vinculada una vez, cada vez es O (n), la eficiencia es menor que HashMap, HashMap es generalmente O (LogN) en el árbol rojo-negro, O (n) en la lista vinculada, posicionamiento directo Es 1

public  put V sincronizada (clave K, valor V) {
         // No se permite que el valor sea nulo 
        si (valor == nulo ) {
             arrojar  nueva NullPointerException (); 
        } 

        // Se asegura de que la clave no esté en la tabla hash. 
        Entrada <? ?,> Tab [] = Tabla;
         // obtener clave hash 
        int de hash = key.hashCode ();
         // obtener el índice correspondiente a la cubeta de hash en la matriz 
        int index = (Hash y 0x7FFFFFFF)% tab.length; 
        @SuppressWarnings ( "sin marcar" )
         // Obtener el encabezado de la lista vinculada en el depósito
        Entrada <K, V> entrada = (Entrada <K, V> ) pestaña [índice];
         // Recorriendo desde el principio 
        para (; entrada! = Nulo ; entrada = entrada.siguiente) {
             // Una vez que los valores hash son iguales y las claves son iguales , Reemplace el valor anterior 
            if ((entry.hash == hash) && entry.key.equals (key)) { 
                V old = entry.value; 
                entry.value = value;
                 return old; 
            } 
        } 
        // si no se encuentra la misma clave , Luego agregue un nuevo nodo 
        addEntry (hash, clave, valor, índice);
         return  null ; 
    }

addEntry

privada  vacío addEntry ( int hachís, llave K, el valor V, int index) { 
        modCount ++ ; 

        Entrada <?,?> Pestaña [] = tabla;
        // 如果 尺寸 超过 了 阈 值 , 进行 rehash 
        if (cuenta> = umbral) {
             // Rehash la tabla si se supera el umbral 
            rehash (); 

            tab = tabla; 
            hash = key.hashCode (); 
            index = (hash & 0x7FFFFFFF)% tab.length; 
        } 

        // Crea la nueva entrada.
        @SuppressWarnings ("sin marcar" ) 
        Entrada <K, V> e = pestaña (Entrada <K, V> ) [índice]; 
        tab [index] = new Entry <> (hash, key, value, e); 
        recuento ++ ; 
    }

obtener

public  get V sincronizado (clave de objeto) { 
        Entry <?,?> tab [] = table;
         int hash = key.hashCode ();
         int index = (hash & 0x7FFFFFFF)% tab.length;
     // atraviesa todos los elementos, ha Si el valor es el mismo que la clave, regrese 
        para (Entry <?,?> E = tab [index]; e! = Null ; e = e.next) {
             if ((e.hash == hash) && e.key. es igual a (clave)) {
                 return (V) e.value; 
            } 
        } 
        return  null ; 
    }

 

Supongo que te gusta

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