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