Estoy de acuerdo con:
- La complejidad general amortizada de O (1)
- una mala
hashCode()
implementación podría dar lugar a múltiples colisiones, lo que significa que, en el peor de los casos, cada objeto va al mismo depósito, por lo tanto, O ( N ) si cada depósito está respaldado por a List
.
- desde Java 8,
HashMap
reemplaza dinámicamente los nodos (lista vinculada) utilizados en cada depósito con TreeNodes (árbol rojo-negro cuando una lista se hace más grande que 8 elementos), lo que resulta en un peor rendimiento de O ( logN ).
Pero, esto NO es toda la verdad si queremos ser 100% precisos. La implementación hashCode()
y el tipo de clave Object
(inmutable / en caché o ser una Colección) también puede afectar la complejidad real en términos estrictos.
Asumamos los siguientes tres casos:
HashMap<Integer, V>
HashMap<String, V>
HashMap<List<E>, V>
¿Tienen la misma complejidad? Bueno, la complejidad amortizada del primero es, como se esperaba, O (1). Pero, por lo demás, también necesitamos calcular hashCode()
el elemento de búsqueda, lo que significa que podríamos tener que atravesar matrices y listas en nuestro algoritmo.
Supongamos que el tamaño de todas las matrices / listas anteriores es k . Entonces, HashMap<String, V>
y HashMap<List<E>, V>
tendrá O (k) complejidad amortizada y, de manera similar, O ( k + logN ) en el peor de los casos en Java8.
* Tenga en cuenta que el uso de una String
clave es un caso más complejo, ya que es inmutable y Java almacena el resultado hashCode()
en una variable privada hash
, por lo que solo se calcula una vez.
/** Cache the hash code for the string */
private int hash; // Default to 0
Pero, lo anterior también tiene su propio peor caso, porque la String.hashCode()
implementación de Java está verificando si hash == 0
antes de computar hashCode
. Pero bueno, hay cadenas no vacías que hashcode
generan un cero, como "f5a5a608", vea aquí , en cuyo caso la memorización podría no ser útil.