Colección detallada colección de mapas

Interfaz de mapa

  • El mapa y la colección existen uno al lado del otro. Se utiliza para guardar datos con una relación de mapeo: clave-valor
  • Tanto la clave como el valor en el mapa pueden ser datos de cualquier tipo de referencia
  • Las claves en el Mapa se almacenan en Set, y no se permite la repetición, es decir, la clase correspondiente al mismo objeto Map debe anular los métodos hashCode () y equals ()
  • Existe una relación unidireccional de uno a uno entre la clave y el valor, es decir, una clase de implementación común única y definida de la interfaz valueMap siempre se puede encontrar a través de la clave especificada: HashMap, TreeMap, LinkedHashMap y Properties. Entre ellos, HashMap es la clase de implementación con la interfaz Map más utilizada.

Métodos comunes de la interfaz del mapa:

método descripción
Colocación de objeto (clave de objeto, valor de objeto) Agregar (o modificar) el valor-clave especificado al objeto de mapa actual
void putAll (Mapa m) Almacene todos los pares clave-valor en m en el mapa actual
Eliminar objeto (clave de objeto) Eliminar el par clave-valor de la clave especificada y devolver el valor
vacío claro () Borrar todos los datos del mapa actual
Obtener objeto (clave de objeto) Obtiene el valor correspondiente a la clave especificada
boolean containsKey (clave de objeto) Ya sea para incluir la clave especificada
boolean containsValue (valor del objeto) Ya sea para incluir el valor especificado
int tamaño () Devuelve el número de pares clave-valor en el mapa.
boolean isEmpty () Determinar si el mapa actual está vacío
booleano es igual a (Objeto obj) Determine si el mapa actual y el objeto de parámetro obj son iguales
Establecer keySet () Devuelve el conjunto de todas las claves
Valores de colección () Devuelve la colección compuesta por todos los valores.
Establecer entrySet () Devuelve la colección Set de todos los pares clave-valor

Una de las clases de implementación de Map: HashMap

  • HashMap es la clase de implementación con la interfaz Map más utilizada.
  • Se permiten claves nulas y valores nulos. Al igual que HashSet, el orden de asignación no está garantizado.
  • El conjunto compuesto por todas las claves es Conjunto: desordenado y no repetible. Por lo tanto, la clase donde se encuentra la clave debe reescribirse: equals () y hashCode ()
  • La colección compuesta por todos los valores es Colección: desordenada y repetible. Por lo tanto, la clase donde se encuentra el valor debe reescribirse: equals ()
  • Un valor-clave constituye una entrada
  • El conjunto de todas las entradas es Conjunto: desordenado y no repetible
  • El criterio para que HashMap juzgue que dos claves son iguales es: las dos claves devuelven verdadero a través del método equals (), y los valores hashCode también son iguales.
  • El criterio para que HashMap juzgue que dos valores son iguales es: dos valores devuelven verdaderos a través del método equals ().

Estructura de almacenamiento HashMap

[Error en la transferencia de la imagen del enlace externo. Es posible que el sitio de origen tenga un mecanismo anti-sanguijuelas. Se recomienda guardar la imagen y subirla directamente (img-40qcI5ou-1614868335362) (assets / image-20210217133622944.png)]

[Error en la transferencia de la imagen del enlace externo. Es posible que el sitio de origen tenga un mecanismo anti-sanguijuelas. Se recomienda guardar la imagen y subirla directamente (img-P0pjLdxg-1614868335367) (assets / image-20210217133639798.png)]

Constantes importantes en el código fuente de HashMap

** DEFAULT_INITIAL_CAPACITY **: la capacidad predeterminada de HashMap, 16

** MAXIMUM_CAPACITY **: la capacidad máxima admitida de HashMap, 2 ^ 30

DEFAULT_LOAD_FACTOR : el factor de carga predeterminado de HashMap es 0,75

TREEIFY_THRESHOLD : la longitud de la lista vinculada en el depósito es mayor que el valor predeterminado, convertido en un árbol rojo-negro 8

UNTREEIFY_THRESHOLD : el nodo almacenado en el árbol rojo-negro en el depósito es menor que el valor predeterminado y se convierte en una lista vinculada

MIN_TREEIFY_CAPACITY : la capacidad mínima de la tabla hash cuando el nodo en el depósito está trepado . (Cuando la cantidad de nodos en el depósito es tan grande que necesita convertirse en un árbol rojo-negro, si la capacidad de la tabla hash es menor que MIN_TREEIFY_CAPACITY, la operación de cambio de tamaño debe realizarse en este momento. El valor de MIN_TREEIFY_CAPACITY es al menos 4 veces mayor que TREEIFY_THRESHOLD.)

tabla : una matriz de elementos de almacenamiento, siempre 2 elevado a n

entrySet : almacena un conjunto de elementos específicos

tamaño : el número de pares clave-valor almacenados en el HashMap

modCount : la cantidad de veces que el HashMap se ha expandido y la estructura ha cambiado.

umbral : el valor crítico de expansión de capacidad, = capacidad * factor de llenado 16 * 0,75 = 12

loadFactor : factor de relleno

Análisis de código fuente antes de JDK1.8

Clase interna en HashMap: Node

static class Node<K,V> implements Map.Entry<K,V> {
    
    
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

    Node(int hash, K key, V value, Node<K,V> next) {
    
    
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    ...
}

La estructura de almacenamiento interno de HashMap es en realidad una combinación de matriz y lista vinculada. Al crear una instancia de un HashMap, el sistema creará una matriz de entrada con una longitud de Capacidad. Esta longitud se denomina Capacidad en la tabla hash. La ubicación donde se pueden almacenar los elementos en esta matriz se denomina "depósito" (depósito), cada depósito tiene su propio índice y el sistema puede encontrar rápidamente los elementos en el depósito de acuerdo con el índice.

Cada bucket almacena un elemento, es decir, un objeto Entry, pero cada objeto Entry puede llevar una variable de referencia para apuntar al siguiente elemento, por lo que en un bucket es posible generar una cadena Entry. Y el elemento recién agregado se usa como el encabezado de la lista vinculada.

El proceso de agregar elementos:

Para agregar entry1 (clave, valor) al HashMap, primero debe calcular el valor hash de la clave en entry1 (calculado de acuerdo con el hashCode () de la clase donde se encuentra la clave). Después de que se procesa el valor hash, se obtiene en la matriz Entry [] subyacente. La ubicación que se almacenará i. Si no hay ningún elemento en la posición i, la entrada1 se agrega directamente. Si la entrada2 ya existe en la posición i (o hay una entrada3, una entrada4 en la lista vinculada), debe utilizar un método de bucle para comparar secuencialmente la clave de la entrada1 con otras entradas. Si los valores hash son diferentes entre sí, la adición directa se realiza correctamente. Si los
valores hash son diferentes, continúe comparando si los dos son iguales. Si el valor devuelto es verdadero, el valor de entrada1 se usa para reemplazar el valor de la entrada cuyo igual es verdadero. Si después de atravesarlo nuevamente, se encuentra que todos los retornos iguales son falsos, entonces la entrada1 aún se puede agregar con éxito. entry1 apunta al elemento de entrada original.

Expansión de HashMap

Cuando hay más y más elementos en el HashMap, la probabilidad de conflictos de hash aumenta cada vez más, porque la longitud de la matriz es fija. Por lo tanto, para mejorar la eficiencia de la consulta, es necesario expandir la matriz HashMap. Después de la expansión de la matriz HashMap, aparece el punto que consume más rendimiento: los datos en la matriz original deben recalcularse en su posición en la nueva matriz y colocada en Go in, esto se redimensiona.

Entonces, ¿cuándo se expandirá HashMap?

Cuando el número de elementos en el HashMap excede el tamaño de la matriz (la longitud total de la matriz, no el tamaño del número en la matriz) * loadFactor, la matriz se expandirá. El valor predeterminado de loadFactor (DEFAULT_LOAD_FACTOR) es 0.75, que es un valor de compromiso. Es decir, por defecto, el tamaño de la matriz (DEFAULT_INITIAL_CAPACITY) es 16, luego cuando el número de elementos en el HashMap excede 16 * 0.75 = 12 (este valor es el valor umbral en el código, también llamado valor crítico), la matriz El tamaño de se expande a 2 * 16 = 32, es decir, se duplica, y luego recalcula la posición de cada elemento en la matriz, y esta es una operación que consume mucho rendimiento, por lo que si ya predecimos el número de elementos en HashMap, luego La cantidad de elementos preestablecidos puede mejorar efectivamente el rendimiento de HashMap.

Análisis de código fuente después de JDK1.8

La estructura de almacenamiento interno de HashMap es en realidad una combinación de matriz + lista vinculada + árbol. Al crear una instancia de un HashMap, initialCapacity y loadFactor se inicializarán. Al colocar el primer par de relaciones de mapeo, el sistema creará una matriz de Nodos con una longitud de initialCapacity. Esta longitud se denomina Capacidad en la tabla hash. La ubicación donde se pueden colocar los elementos almacenados en la matriz se denomina "depósito". Cada depósito tiene su propio índice, y el sistema puede encontrar rápidamente los elementos del depósito en función del índice.

Cada depósito almacena un elemento, es decir, un objeto Nodo, pero cada objeto Nodo puede llevar una variable de referencia junto a apuntar al siguiente elemento. Por lo tanto, en un depósito, es posible generar una cadena Nodo. También puede ser un objeto TreeNode. Cada objeto TreeNode puede tener dos nodos hoja a la izquierda y a la derecha. Por lo tanto, en un cubo, es posible generar un árbol TreeNode. El elemento recién agregado sirve como el último de la lista vinculada, o el nodo hoja del árbol.

Entonces, ¿cuándo se expandirá HashMap y tendrá forma de árbol?

Cuando el número de elementos en el HashMap excede el tamaño de la matriz (la longitud total de la matriz, no el tamaño del número en la matriz) * loadFactor, la matriz se expandirá. El valor predeterminado de loadFactor (DEFAULT_LOAD_FACTOR) es 0,75, que es un valor de compromiso. En otras palabras, por defecto, el tamaño de la matriz (DEFAULT_INITIAL_CAPACITY) es 16, luego, cuando el número de elementos en el HashMap excede 16 * 0.75 = 12 (este valor es el valor umbral en el código, también llamado valor crítico), el matriz El tamaño de se expande a 2 * 16 = 32, es decir, se duplica, y luego se recalcula la posición de cada elemento en la matriz, y esta es una operación que consume mucho rendimiento, por lo que si hemos predicho el número de elementos en el HashMap, luego El número de elementos preestablecidos puede mejorar efectivamente el rendimiento de HashMap.

Cuando el número de objetos en una de las cadenas en el HashMap llega a 8, si la capacidad no llega a 64, entonces el HashMap se expandirá primero. Si ha llegado a 64, entonces la cadena se convertirá en un árbol, y el tipo de nodo se cambiará de Nodo a tipo TreeNode. Por supuesto, si después de eliminar la relación de mapeo, la próxima vez que el método de cambio de tamaño determine que el número de nodos en el árbol es menor que 6, también convertirá el árbol en una lista vinculada.

¿Se puede modificar la clave de la relación de mapeo? Respuesta: No modifique la
relación de mapeo y almacene el valor hash de la clave en el HashMap, de modo que no necesite volver a calcular el valor hash de cada entrada o nodo (TreeNode) cada vez que busque, si ya lo ha hecho coloque la relación de mapeo en el mapa, y luego modifique el atributo clave, y este atributo participa en el cálculo del valor del código hash, luego hará que la coincidencia falle.

Resumen: JDK1.8 en comparación con los cambios anteriores:

  1. HashMap map = new HashMap (); // Por defecto, no se crea primero una matriz de longitud 16
  2. Cuando se llama a map.put () por primera vez, se crea una matriz de longitud 16
  3. La matriz es de tipo Nodo, llamada Tipo de entrada en jdk7
  4. Al formar una estructura de lista vinculada, el par clave-valor recién agregado se encuentra al final de la lista vinculada (siete altibajos)
  5. Cuando la longitud de la lista vinculada en la posición de índice especificada de la matriz es> 8 y la longitud de la matriz en el mapa es> 64, todos los pares clave-valor en esta posición de índice se almacenan en un árbol rojo-negro.

Comparado con jdk7, jdk8 es diferente en la implementación subyacente:

  • new HashMap (): la capa inferior no creó una matriz de longitud 16
  • La matriz subyacente de jdk 8 es: Nodo [], no Entrada []
  • Cuando se llama al método put () por primera vez, la capa inferior crea una matriz de longitud 16
  • La estructura subyacente de jdk7 solo tiene: matriz + lista vinculada. La estructura subyacente en jdk8: matriz + lista vinculada + árbol rojo-negro.
  • Al formar una lista enlazada, siete altibajos (jdk7: los elementos nuevos apuntan a elementos antiguos. Jdk8: los elementos antiguos apuntan a elementos nuevos)
  • Cuando el número de datos en una posición de índice de la matriz en forma de lista vinculada> 8 y la longitud de la matriz actual> 64, los datos en esta posición de índice se cambian para usar el almacenamiento de árbol rojo-negro.

Preguntas de entrevista:

Cuénteme acerca de su conocimiento de los métodos put / get en HashMap. Si lo entiende, ¿habla sobre el mecanismo de expansión de HashMap? ¿Cuál es el tamaño predeterminado? ¿Cuál es el factor de carga (o relación de llenado)? ¿Cuál es el umbral de rendimiento (o umbral, umbral)?

¿Cuál es el impacto del valor del factor de carga en HashMap?

  • El tamaño del factor de carga determina la densidad de datos del HashMap.
  • Cuanto mayor sea el factor de carga, mayor será la densidad, mayor será la probabilidad de colisiones y más larga será la lista enlazada en la matriz. Esto aumentará el número de comparaciones durante la consulta o inserción y reducirá el rendimiento.
  • Cuanto menor sea el factor de carga, más fácil será activar la expansión. Cuanto menor sea la densidad de datos, menor será la probabilidad de colisión, menor será la lista vinculada en la matriz, menor será el número de comparaciones entre consultas e inserciones, y mayor la actuación. Pero se desperdiciará una cierta cantidad de espacio de contenido. Además, la expansión frecuente también afectará al rendimiento. Se recomienda inicializar un espacio más grande.
  • De acuerdo con la referencia y la experiencia de investigación en otros idiomas, se considerará que el factor de carga está establecido en 0.7 ~ 0.75, en este momento la duración promedio de búsqueda es cercana a una constante.

Categoría de implementación del mapa dos: LinkedHashMap

  • LinkedHashMap es una subclase de HashMap
  • Según la estructura de almacenamiento de HashMap, se utilizan un par de listas doblemente enlazadas para registrar el orden de adición de elementos.
  • Similar a LinkedHashSet, LinkedHashMap puede mantener la iteración de Map
  • Orden: el orden de iteración es coherente con el orden de inserción del par clave-valor

Clase interna en LinkedHashMap: Entrada

static class Entry<K,V> extends HashMap.Node<K,V> {
    
    
    Entry<K,V> before, after;//能够记录添加的元素的先后顺序
    Entry(int hash, K key, V value, Node<K,V> next) {
    
    
        super(hash, key, value, next);
    }
}

Categoría de implementación del mapa tres: TreeMap

  • Cuando TreeMap almacena pares clave-valor, debe ordenarse según pares clave-valor. TreeMap puede garantizar que todos los pares clave-valor estén en un estado ordenado.
  • La capa inferior de TreeSet utiliza una estructura de árbol rojo-negro para almacenar datos
  • Clasificación de la clave de TreeMap:
    • Orden natural: todas las claves de TreeMap deben implementar una interfaz comparable y todas las claves deben ser objetos de la misma clase; de ​​lo contrario, se lanzará ClasssCastException
    • Clasificación personalizada: al crear un TreeMap, pase un objeto Comparador, que es responsable de clasificar todas las claves en el TreeMap. En este momento, no es necesario que Map Key implemente la interfaz Comparable
  • El criterio para que TreeMap juzgue que dos claves son iguales: dos claves
    devuelven 0 mediante el método compareTo () o el método compare ().

Categoría de implementación del mapa cuatro: Hashtable

  • Hashtable es una clase de implementación de mapas antigua, proporcionada por JDK1.0. A diferencia de HashMap, Hashtable es seguro para subprocesos
  • El principio de implementación de Hashtable es el mismo que HashMap y las funciones son las mismas. La estructura de la tabla hash se usa en la capa inferior y la velocidad de consulta es rápida. En muchos casos, se puede usar mutuamente
  • A diferencia de HashMap, Hashtable no permite el uso de null como clave y valor
  • Al igual que HashMap, Hashtable no puede garantizar la secuencia de pares clave-valor.
  • El criterio para que Hashtable juzgue que dos claves son iguales y dos valores son iguales es consistente con HashMap

Categoría de implementación del mapa cinco: Propiedades

  • La clase Propiedades es una subclase de Hashtable, este objeto se usa para procesar archivos de propiedades
  • Dado que la clave y el valor en el archivo de propiedades son ambos tipos de cadena, la clave y el valor en Propiedades son ambos tipos de cadena
  • Al acceder a los datos, se recomienda utilizar el método setProperty (clave de cadena, valor de cadena) y el método getProperty (clave de cadena)
Properties pros = new Properties();
pros.load(new FileInputStream("jdbc.properties"));
String user = pros.getProperty("user");
System.out.println(user);

Supongo que te gusta

Origin blog.csdn.net/qq_44346427/article/details/110729397
Recomendado
Clasificación