Je suis d'accord avec:
- la complexité amortie générale de O (1)
- une mauvaise
hashCode()
implémentation peut entraîner plusieurs collisions, ce qui signifie que dans le pire des cas, chaque objet va dans le même compartiment, donc O ( N ) si chaque compartiment est sauvegardé par un List
.
- depuis Java 8,
HashMap
remplace dynamiquement les nœuds (liste chaînée) utilisés dans chaque bucket par TreeNodes (arbre rouge-noir lorsqu'une liste dépasse 8 éléments) résultant en une pire performance de O ( logN ).
Mais ce n'est PAS la vérité si nous voulons être précis à 100%. L'implémentation hashCode()
et le type de clé Object
(immuable / mis en cache ou étant une collection) peuvent également affecter la complexité réelle en termes stricts.
Supposons les trois cas suivants:
HashMap<Integer, V>
HashMap<String, V>
HashMap<List<E>, V>
Ont-ils la même complexité? Eh bien, la complexité amortie du premier est, comme prévu, O (1). Mais, pour le reste, nous devons également calculer hashCode()
l'élément de recherche, ce qui signifie que nous pourrions devoir parcourir des tableaux et des listes dans notre algorithme.
Supposons que la taille de tous les tableaux / listes ci-dessus est k . Ensuite, HashMap<String, V>
et HashMap<List<E>, V>
aura une complexité amortie O (k) et de même, le pire des cas O ( k + logN ) en Java8.
* Notez que l'utilisation d'une String
clé est un cas plus complexe, car elle est immuable et Java met en cache le résultat de hashCode()
dans une variable privée hash
, donc elle n'est calculée qu'une seule fois.
/** Cache the hash code for the string */
private int hash; // Default to 0
Mais ce qui précède a également son pire cas, car l' String.hashCode()
implémentation de Java vérifie si hash == 0
avant de calculer hashCode
. Mais bon, il y a des chaînes non vides qui produisent un hashcode
zéro, comme "f5a5a608", voir ici , auquel cas la mémorisation peut ne pas être utile.