Sono d'accordo con:
- la complessità generale ammortizzata di O (1)
- una cattiva
hashCode()
implementazione potrebbe comportare più collisioni, il che significa che nel caso peggiore ogni oggetto va nello stesso bucket, quindi O ( N ) se ogni bucket è supportato da a List
.
- da Java 8,
HashMap
sostituisce dinamicamente i Nodi (elenco collegato) utilizzati in ogni bucket con TreeNodes (albero rosso-nero quando un elenco diventa più grande di 8 elementi) con conseguenti prestazioni peggiori di O ( logN ).
Ma questo NON è la verità se vogliamo essere precisi al 100%. L'implementazione hashCode()
e il tipo di chiave Object
(immutabile / memorizzata nella cache o essendo una raccolta) potrebbe anche influire sulla complessità reale in termini rigorosi.
Supponiamo che i seguenti tre casi:
HashMap<Integer, V>
HashMap<String, V>
HashMap<List<E>, V>
Hanno la stessa complessità? Bene, la complessità ammortizzata della prima è, come previsto, O (1). Ma, per il resto, dobbiamo anche calcolare hashCode()
l'elemento di ricerca, il che significa che potremmo dover attraversare matrici ed elenchi nel nostro algoritmo.
Supponiamo che la dimensione di tutti gli array / elenchi sopra sia k . Quindi, HashMap<String, V>
e HashMap<List<E>, V>
avrà la complessità ammortizzata O (k) e allo stesso modo, il caso peggiore di O ( k + logN ) in Java8.
* Si noti che l'utilizzo di una String
chiave è un caso più complesso, poiché è immutabile e Java memorizza nella cache il risultato hashCode()
in una variabile privata hash
, quindi viene calcolata una sola volta.
/** Cache the hash code for the string */
private int hash; // Default to 0
Ma quanto sopra ha anche il suo caso peggiore, poiché l' String.hashCode()
implementazione di Java sta verificando se hash == 0
prima dell'informatica hashCode
. Ma ehi, ci sono stringhe non vuote che producono uno hashcode
zero, come "f5a5a608", vedi qui , nel qual caso la memoizzazione potrebbe non essere utile.