2
votes

Détaillez le big-O de Hashmap - méthode put () par du code réel en Java 8

Je suis novice en algorithme. J'ai lu et je suis conscient que big-O de put (clé K, valeur V) dans Hashmap est O (1). Quand je suis allé au cœur de la classe HashMap

 for (int binCount = 0; ; ++binCount) {

Comme vous pouvez le voir, lors de l'ajout d'un nouvel élément à hashmap, il itérera max n (tous les éléments de hashmap) avec "For Loop" ci-dessus:

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        //...
    if ((p = tab[i = (n - 1) & hash]) == null)
        //...
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            // ...
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    // ...
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
           // ...
        }
    }
    ...
    return null;
}

Maintenant, big-O de For Loop ici est O (n) -> Pourquoi big-O de put (K key, V value ) dans HashMap peut être O (1)? Où est-ce que je comprends mal?

Merci beaucoup.


1 commentaires

pourquoi posez-vous exactement la même question deux fois? Votre premier a déjà été fermé comme doublon


3 Réponses :


2
votes

L'idée est qu'un seul bac de la table de hachage a un nombre constant attendu d'éléments. Par conséquent, la boucle que vous avez mentionnée s'exécuterait dans le temps prévu O (1) (en supposant que le hashCode () de vos clés n'est pas terrible).

Plus précisément, pour l'implémentation HashMap , il existe un facteur de charge dont la valeur est de 0,75 par défaut. Cela signifie qu'en moyenne, chaque casier du HashMap doit avoir <= 0,75 éléments (une fois qu'il y a plus d'entrées de facteur de charge * nombre de casiers dans le HashMap , le nombre de cases est doublé pour maintenir cet invariant). Par conséquent, la boucle mentionnée devrait avoir une seule itération en moyenne.


0 commentaires

2
votes

Le HashMap est en fait une collection (soutenue par un tableau) de buckets qui sont soutenus par un arbre rouge-noir (à partir de Java 8). Si vous avez une fonction de hachage très médiocre qui place tous les éléments dans le même bac, les performances se dégraderont en O(log(n))

De Baeldung ,

HashMap a une complexité O (1), ou complexité en temps constant, pour mettre et obtenir les éléments. Bien sûr, de nombreuses collisions pourraient dégrader les performances en complexité temporelle O (log (n)) dans le pire des cas, lorsque tous les éléments atterrissent dans un seul compartiment. Ceci est généralement résolu en fournissant une bonne fonction de hachage avec une distribution uniforme.

À partir de la documentation , < / p>

Cette implémentation fournit des performances en temps constant pour les opérations de base (get et put), en supposant que la fonction de hachage répartit correctement les éléments entre les buckets.


0 commentaires

0
votes

La notation Big-O a à voir avec les performances d'une fonction par rapport au nombre d'éléments sur lesquels elle opère. Le simple fait d'avoir une boucle for ne fait pas soudainement augmenter les performances d'une recherche Hashmap d'un montant fixe pour chaque élément de la Hashmap .

Il existe des modèles dans Big-O notation. Les boucles sur l'ensemble des éléments sont O (n) mais les boucles en général ne signifient pas que la recherche est O (n) . Pour illustrer, je vais utiliser l'exemple (idiot) ci-dessous

Fonction avec O (1) performance

  public void add_still_o_one(int x, int y) {
     int[2] numbers;
     numbers[0] = x; 
     numbers[1] = y;
     int result = 0;
     for (int index = 0; index < numbers.length; index++) {
        result += numbers[index];
     }
     return result;
  }

Fonction avec des performances O (1) , avec des boucles ajoutées

  public void add_o_one(int x, int y) {
     return x + y;
  }

Alors que je m'attendrais à ce que ce dernier soit un peu moins efficace, il n'y a aucun moyen de modifier sa vitesse d'exécution en choisissant des nombres différents (ou plus). Par conséquent, il s'agit toujours de O(1).

Dans votre Hashmap, le bouclage sur les listes de seaux modifie la vitesse par rapport à l'entrée; mais cela ne le modifie pas d'une quantité constante pour chaque élément. De plus, la plupart des implémentations de hashmap augmentent leur taille de compartiment à mesure que la carte se remplit. Cela signifie que ce que vous voyez n'est pas un cas courant, et est susceptible d'être rarement appelé (à moins que vous n'implémentiez un très mauvais hashcode).

Bien que vous puissiez considérer que "quelque chose de plus grand que O (1) ", il est très difficile de faire fonctionner le code d'une manière incompatible avec O (1 ) à moins que vous ne cassiez spécifiquement l'algorithme (en fournissant des objets qui ont tous la même valeur, par exemple).


0 commentaires