Explication détaillée du principe de mise en œuvre sous-jacent de HashMap, essai stéréotypé JAVA

Le principe de mise en œuvre sous-jacent :

HashMapLa structure de données sous-jacente Java8était sous la forme avant 数组+链表, et sous la forme d'un tableau + liste liée + arbre rouge-noir après java8 ;

Une fois créé, la longueur par défaut est de 0 et la longueur sera étendue pour la première fois lorsque la méthode put est appelée. La longueur sera de 16. Lorsque le facteur de charge en us dépasse le facteur de charge par défaut de 0,75, la capacité 元素个数/总容量sera élargi, et la capacité sera augmentée de 2 fois à chaque fois ;

Lors de l'ajout d'un objet, il calcule d'abord la clé hashCode(), puis appelle la HashMapméthode hash()de hachage secondaire. Enfin, calculez l'indice du compartiment en prenant la longueur du tableau de module. 在jdk1.8后优化了取模计算方式Si le contenu actuel de l'indice est vide, ajoutez-le directement. S'il n'est pas vide Ensuite, jugez si la clé hashCode()est cohérente, sinon, recherchez en arrière jusqu'à ce que le contenu de l'indice soit vide, ajoutez-le, jdk1.8是尾部追加,jdk1.8之前是头部追加s'il est cohérent, effectuez equalsdes calculs pour déterminer si le contenu est le même, si c'est le même, remplacez le valeur, et si elle est différente, continuer à comparer avec le contenu suivant ;

Après l'ajout, lorsque la longueur de la liste chaînée actuelle est supérieure à 8 et que la capacité du tableau est supérieure ou égale à 64, la liste chaînée sera convertie en un arbre rouge-noir. Si le nombre d'éléments dans le array est supérieur au nombre d'éléments dans le tableau une fois l'ajout terminé ou converti en un arbre rouge-noir, il sera appelé pour se développer et tous les buckets seront téléchargés . La marque 数组长度*负载因子est resize()recalculée 重新取模当前新的容量计算桶下标.

question d'élévation :

structure de données sous-jacente

  1. La structure de données sous-jacente avant java8 est tableau + liste liée
  2. La structure de données sous-jacente après java8 est tableau + liste liée + arbre rouge-noir

Pourquoi utiliser un sapin rouge-noir ?

Car si vous n'utilisez pas d'arbre rouge-noir, si vous voulez accéder à une donnée, si la liste chaînée est très longue, il faut la comparer depuis le début, ce qui affectera les performances de HashMap 时间复杂度为O(n). un arbre rouge-noir, il sera alloué en fonction de la taille (celui qui est plus grand que le nœud parent est placé à droite. , celui qui est plus petit que le nœud parent est placé à gauche), il suffit de comparer la taille via le code de hachage (si le code de hachage est le même, comparez la valeur de la chaîne) pour omettre les comparaisons redondantes et localiser l'élément que nous devons rechercher 时间复杂度为O(log₂ⁿ).

Pourquoi ne pas le transformer en arbre rouge-noir quand il se dresse ?

Parce qu'au début, lorsque la longueur de la liste chaînée est courte, ses performances sont meilleures que celles de l'arbre rouge-noir.Ce n'est que lorsque la liste chaînée est longue que les performances ne sont pas aussi bonnes que celles de l'arbre rouge-noir .
Et la structure de données de la liste chaînée est Node, et la structure de données de l'arbre rouge-noir est TreeNode. Les variables membres du TreeNode sont bien plus nombreuses que celles de la liste chaînée, donc la mémoire occupée par l'arbre rouge-noir est également supérieur à celui de la liste chaînée.

Pourquoi le seuil d'arborescence est de 8 ?

Normalement, si le hachage est suffisamment aléatoire et que le facteur de charge est de 0,75, la longueur de la liste chaînée dans le HashMap est rarement supérieure à 8. Choisir 8 revient à rendre la conversion en un arbre rouge-noir moins probable. l'arborescence rouge-noire doit être convertie en Pour éviter les attaques DoS et empêcher la dégradation des performances lorsque la liste chaînée est trop longue, l'arborescence doit être accidentelle.

Quand deviendra-t-elle une liste chaînée ?

Lorsque la longueur est inférieure ou égale à 6, il sera converti en un
removenœud d'arbre rouge-noir de liste chaînée. Si l'un des éléments root, root.left, root.right, root.left.left est nul, il sera également convertie en liste chaînée root表示根节点,left表示左边的节点,right表示右边的节点.

Comment l'indice d'indice est-il calculé ?
Avant 1.8 : la valeur de hachage secondaire prend la longueur du tableau modulo.
1.8 : Calculez le hashCode() de l'objet, puis appelez la méthode hash() de HashMap pour le hachage secondaire, et enfin & 位运算(capacity 当前容量- 1) pour obtenir l'index.

Pourquoi le hachage secondaire ?

Si les valeurs de poids faible du HashCode sont les mêmes, le problème d'une distribution de hachage inégale se produira si le deuxième hachage n'est pas effectué.Afin de garantir que les données sont uniformément réparties dans le tableau et d'éviter la situation où le liste chaînée est trop longue, nous devons effectuer une deuxième opération de hachage.

Pourquoi la capacité du tableau 2 est-elle à la nième puissance ?

Lors du calcul de l'index, s'il s'agit de la nième puissance de 2, vous pouvez utiliser l'opération bit-AND au lieu du module, ce qui est plus efficace ; lors de l'expansion, utilisez le deuxième hachage & oldCap ==0 L'élément reste dans l' 旧的容量original position, sinon se déplace vers la nouvelle position 旧位置+oldCap.

Que se passe-t-il si la capacité du tableau n'est pas la puissance de 2 ?

Les éléments mentionnés ci-dessus concernent tous la méthode d'optimisation lorsque la capacité est la nième puissance de 2. Par exemple, la capacité de Hashtable n'est pas la nième puissance de 2. On ne peut pas dire quelle conception est la meilleure. a intégré divers facteurs. Au final, nous avons choisi d'utiliser la puissance de 2 comme capacité.

Si tous les nombres stockés dans notre tableau sont des nombres pairs, la capacité de 2 à la puissance n les fera tous être distribués dans des positions paires.

Si vous voulez rechercher une efficacité plus élevée, vous pouvez utiliser la nième puissance de 2 comme capacité, et si vous voulez une meilleure distribution de hachage, vous pouvez choisir un nombre premier comme capacité.

Quelle est la différence entre la méthode put dans jdk1.7 et jdk1.8 ?

Lorsque la liste chaînée insère un nœud, 1.7 est la méthode d'insertion de tête et 1.8 est la méthode d'insertion de queue ;

1.7 est de se développer lorsque le seuil est atteint et que le contenu de la position courante de l'indice de calcul n'est pas vide, et 1.8 est de se développer lorsque le seuil est dépassé ;

1.8 Lors de l'expansion et du calcul de l'indice de nœud, 扩容时 hash & oldCapl'ancienne capacité sera optimisée==0的元素留在原来位置,否则新位置=旧位置+oldCap。 ;

Pourquoi le facteur de charge est-il par défaut à 0,75 f ?

  1. Bon compromis entre l'utilisation de l'espace et le temps de requête
  2. Si elle est supérieure à cette valeur, l'espace est économisé, mais la liste chaînée sera plus longue et affectera les performances
  3. S'il est inférieur à cette valeur, les conflits seront réduits, mais l'expansion sera plus fréquente et prendra plus de place

Quels sont les problèmes sous le multithreading HashMap ?

Lorsque jdk 1.7, il y aura un problème de chaîne morte d'expansion ;

Le problème de l'écrasement de la valeur de l'opération d'écriture multithread ;

La clé peut-elle être nulle ? Quelles sont les exigences pour un objet en tant que clé ?

La clé de HashMap peut être nulle, mais les autres implémentations de Map ne le sont pas ;

En tant qu'objet clé, hashCode et equals doivent être implémentés, et le contenu de la clé ne peut pas être modifié (immuable) ;

Je suppose que tu aimes

Origine blog.csdn.net/TangBoBoa/article/details/130411566
conseillé
Classement