HashMapEst basé sur une table de hachage,Chaque élément est unkey-valueC'est exact.,Son intérieur résout les conflits au moyen d'une liste à chaîne unique,Capacité insuffisante(Dépassement du seuil)Heure,De même, la croissance automatique.
HashMapN'est pas Thread - SAFE,Uniquement pour les environnements monothreadés,Un environnement Multi - threadé peut utiliser des paquets simultanésconcurrentHashMap
HashMap C'est fait.SerializableInterface,Il supporte donc la sérialisation,C'est fait.CloneableInterface,Peut être cloné
HashMapEst basé sur une table de hachageMapMise en œuvre asynchrone de l'interface.Cette implémentation fournit toutes les opérations de cartographie optionnelles,Et permet l'utilisationnullValeur etnullClé.Cette classe ne garantit pas l'ordre des cartes,En particulier, il ne garantit pas que l'ordre est constant.
Java8Cette mise en œuvre sous - jacente est optimisée,Comme l'introduction de la structure de l'arbre Rouge et noir pour résoudre les collisions de hachage
2 HashMapStructure des données pourInJavaMoyenne,Les structures de base sont les suivantes:,L'un est un tableau,L'autre est un pointeur analogique(Références),Toutes les structures de données peuvent être construites avec ces deux structures de base,HashMapEt ça ne fait pas exception.
HashMapEn fait, c'est un"Hachage de la liste de liens"Structure des données pour,C'est - à - dire une combinaison de tableaux et de listes liées.

HashMapLa structure principale est similaire à un tableau,Ajouter une valeur parkeyDéterminer l'emplacement de stockage.
Chaque emplacement est unEntryStructure des données pour,Cette structure peut constituer une liste de liens.
En cas de conflit,Même chose.hashLes paires de valeurs clés forment une liste de liens.
Ce tableau + Les combinaisons de listes enchaînées donnent de bons résultats dans la plupart des cas ,Java6、7C'est comme ça que ça a été conçu.
Et pourtant,Dans des cas extrêmes,Groupe I(Par exemple, bien conçu)Les deux paires de valeurs clés sont en conflit,La structure de hachage devient alors une liste liée,FaireHashMapChute brutale des performances.
Donc, dansJava8Moyenne,HashMapL'implémentation de la structure devient un tableau+Liste des liens+Arbre Rouge et noir

Comme vous pouvez le voir,,HashMapLe rez - de - chaussée est une structure de tableau
Chaque élément du tableau est une autre liste liée
Quand un nouveauHashMapHeure,Un tableau est initialisé.
HashMapUtilisez trois grands ensembles et trois itérateurs pour les sonderKey、ValueEtEntryObjet
4 Analyse des sourcesParce que la longueur moyenne de recherche d'un arbre Rouge et noir estlog(n),La longueur est8Quand,La longueur moyenne de la recherche est3,Si vous continuez à utiliser la liste liée,La longueur moyenne de la recherche est8/2=4,C'est ce qui rend nécessaire la conversion en arbre
Longueur de la liste si elle est inférieure ou égale à6,6/2=3,Même si c'est rapide,Mais le temps de conversion en structure d'arbre et de génération d'arbre ne sera pas trop court
Il y a d'autres options6Et8,Il y a une différence entre7Peut efficacement empêcher les listes liées et les arbres de changer fréquemment
Supposons que,Si le nombre de listes conçues dépasse8La liste liée est convertie en structure arborescente,Le nombre de listes liées est inférieur à8Ensuite, la structure de l'arbre est convertie en liste liée,Si unHashMapInsertion continue、Supprimer l'élément,Nombre de listes liées8Errer à gauche et à droite,La liste des liens d'arbre se produit fréquemment、Arbre de rotation de la liste liée,Ça va être inefficace..
- Au - dessustableSizeForÀ quoi bon??
tableSizeForLa méthode garantit que la valeur de retour de la fonction est supérieure ou égale à un paramètre donnéinitialCapacityLe plus petit2La valeur numérique de la puissance de
On peut voir que la méthode est une série d'opérations binaires
a |= b équivalent à a = a|b
Analyse ligne par ligne
- int n = cap - 1
Donnécap Moins 1,Pour éviter les paramètrescapC'est ça.2Puissance de,C'est comme ça.,Après un suivi,capVa devenir2 * cap,Ce n'est pas ce que nous attendions
- n |= n >>> 1
n >>> 1 : nDéplacement à droite non signé1Bits,C'est - à - dire:nBinaire supérieur1À droite
n | (n >>> 1) Cause nBinaire élevé2La valeur du BIT est1
Pour l'instantnHaut1~2Tous les bits sont1
- n |= n >>> 2
nContinuer sans symbole à droite2Bits
n | (n >>> 2) CausenHaute représentation binaire3~4Les valeurs de bitpass sont toutes1
Pour l'instantnHaut1~4Tous les bits sont1
- n |= n >>> 4
nContinuer sans symbole à droite4Bits
n | (n >>> 4) CausenHaute représentation binaire5~8Les valeurs de bitpass sont toutes1
Pour l'instantnHaut1~8Tous les bits sont1
- n |= n >>> 8
nContinuer sans symbole à droite8Bits
n | (n >>> 8) CausenHaute représentation binaire9~16Les valeurs de bitpass sont toutes1
Pour l'instantnHaut1~16Tous les bits sont1
Comme vous pouvez le voir,,Peu importecap(cap < MAXIMUM_CAPACITY )Quelle est la valeur de,Après les calculs ci - dessus,Tous les bits binaires de sa valeur seront1.Ajouter1,Cette valeur doit être2Puissance de.
Bien sûr, si la valeur calculée est supérieure àMAXIMUM_CAPACITY,Sélection directeMAXIMUM_CAPACITY.

Jusqu'ici.tableSizeForComment garantircapPour2La puissance de,Alors la question se pose
4.1PourquoicapPour rester2Puissance de?Principalement avecHashMapLe stockage de données dans.
InJava8Moyenne,HashMapMoyennekeyDeHashValeur parHash(key)La méthode calcule

HashMapDonnées stockées danstableDeindexC'est parkeyDeHashValeur déterminée.
InHashMapLors du stockage des données,Nous nous attendons à ce que les données soient réparties uniformément,Pour éviter les conflits de hachage.
Naturellement, nous pensons à l'utiliser%Pour réaliser notre vision
Prends le reste(%)Fonctionnement : Si le diviseur est2La puissance de est égale à la somme de son diviseur moins un(&)Fonctionnement.
C'est pourquoi il fautcapPour2Puissance de.Revenez voirtableDeindexRègles de calcul pour:
C'est pourquoi il fautcapPour2Puissance de.Revenez voirtableDeindexRègles de calcul pour:
Équivalent à:
Fonctionnement binaire&,Par rapport à%,Capable d'améliorer l'efficacité du calcul,C'est ça.capLa valeur de est requise pour2La cause de la puissance


Node<K,V> La classe estHashMapClasse interne statique en,RéalisationMap.Entry<K,V>Interface.DéfinikeyClé、valueValeur、nextNoeud,C'est - à - dire qu'une liste unidirectionnelle est formée entre les éléments.
4.3 TreeNodeLa structure de l'arbre Rouge et noir contient、Après、Gauche.、Noeud droit,Et si le drapeau est rouge et noir
Cette structure estJava8Nouveau
Java 8Fonction d'optimisation de la valeur de hachage dans

Juste une fois16Déplacement à droite Xor
key.hashCode()La fonction appellekeyFonction de hachage fournie avec le type de valeur clé,RetourintValeur de hachage de type
En théorie, la valeur de hachage estintType,Si vous accédez directement à la valeur de hachage comme indiceHashMapSi le tableau principal,Considérant que2Décimal32Bits signésintPortée approximative40Un espace de cartographie de milliards.Tant que les fonctions de hachage sont cartographiées uniformément et vaguement,Il est difficile d'entrer en collision avec une application générale.
Mais le problème, c'est que40Un tableau de milliards de longueurs,Il n'y a pas de mémoire..HashMapLa taille initiale du tableau avant l'expansion16,Donc cette valeur de hachage ne peut pas être utilisée directement.
Il faut d'abord modéliser la longueur du tableau avant de l'utiliser,Le reste obtenu peut être utilisé pour accéder à l'indice Array
L'opération modulaire dans le code source est de faire une valeur de hachage et la longueur du tableau"Avec"Fonctionnement

Ça explique aussi pourquoiHashMapLa longueur du tableau pour2Toute la puissance de
Parce que...(Longueur du tableau-1)Exactement l'équivalent d'un“Masque bas”
“Avec”Le résultat de l'opération est que tous les bits supérieurs de la valeur de hachage sont réinitialisés à zéro,Seules les valeurs inférieures sont réservées,Utilisé pour accéder à un index de tableau
En longueur initiale16Par exemple,16-1=15
2La représentation décimale est00000000 00000000 00001111
Et une certaine valeur de hachage“Avec”Les opérations sont les suivantes,Le résultat est que les quatre chiffres les plus bas ont été tronqués

Mais c'est là que la question se pose,De cette façon, même si ma distribution des valeurs de hachage est encore lâche,Si seulement les derniers chiffres,Les collisions peuvent aussi être graves
À ce moment - là.“Fonction de perturbation”La valeur de
Déplacement à droite16Bits,Exactement.32La moitié,La moitié haute et la moitié basse de soi font Xor,C'est pour mélanger l'originalhashCodeHaut et bas de,Pour augmenter le caractère aléatoire de la position inférieure
Et la partie inférieure du mélange dope une partie de la caractéristique supérieure,Cette information de haut niveau est conservée sous une forme déguisée.
indexLa règle de calcul pour
newCap- Oui.2Puissance,Alors...newCap - 1Tous les sommets de0
Sie.hashLa valeur n'utilise que sa proprehashcode,indexSeulement avece.hashLe bas de&Fonctionnement.C'est comme ça.,indexLa valeur de n'a qu'une faible implication dans l'opération,Le Haut n'a aucun sens de l'existence,Il y a donc un risque de conflit de hachage
Donc je calculekeyDehashCodeHeure,Avec son proprehashCodeAu lieu d'être bas16BIT pour Xor
C'est ce qui amène les cadres supérieurs àindexDans les calculs de,C'est - à - dire réduire le risque de conflit de hachage sans trop de problèmes de performance
4.5 PutMéthodes

①.Déterminer la valeur de la clé par rapport au tableautable[i]Est vide ou estnull,Sinon, exécutezresize()Pour agrandir
②.Selon la valeur de la clékeyCalculhashValeur de l'index du tableau inséréi,Sitable[i]==null,Ajouter un nouveau noeud directement,Direction⑥,Sitable[i]Pas vide,Direction③
③.Jugementtable[i]Le premier élément dekeyC'est pareil,Si la même couverture directevalue,Sinon, tournez④,La même chose ici, c'est quehashCodeEtequals
④.Jugementtable[i] Oui NontreeNode,C'est - à - dire:table[i] Si c'est un arbre Rouge et noir,Si c'est un arbre Rouge et noir,Insérez la paire de clés directement dans l'arbre,Sinon, tournez⑤
⑤.Traverséetable[i],Déterminer si la longueur de la liste est supérieure à8,Plus grand que8Convertit la liste en arbre Rouge et noir,Insérer dans l'arbre Rouge et noir,Sinon, insérez la liste liée;Si vous trouvezkeyUne dérogation directe existe déjàvalueC'est tout.
⑥.Après insertion réussie,Déterminer le nombre réel de paires de valeurs cléssizeSi la capacité maximale est dépasséethreshold,Si plus de,Mise en œuvreresize()Expansion de la capacité
.Dans le constructeur, tout au plus, c'est juste régléinitialCapacity、loadFactorValeur de,Pas d'initialisationtable,tableLe travail d'initialisation pourputDans la méthode.
4.6 resize
Expansion de la capacité(resize)Est de recalculer la capacité,VersHashMapAjout continu d'éléments à l'objet,Lorsque le tableau interne ne peut pas charger plus d'éléments,Il faut agrandir le tableau.
Bien sûr.JavaLe tableau ne s'agrandit pas automatiquement,La méthode consiste à utiliser un nouveau tableau au lieu d'un tableau existant de petite capacité
remove(key) Méthodes Et remove(key, value) Les méthodes sont toutes appeléesremoveNodePour supprimer un élément
4.8 getInJDK1.7Et dans les versions précédentes,HashMapIl n'y a pas d'implémentation d'arbre Rouge et noir,InJDK1.8Un arbre Rouge et noir a été ajouté pour empêcher les attaques de collision de table de hachage,Lorsque la longueur de la chaîne est8Heure,À temps pour se transformer en arbre Rouge et noir,AméliorationmapEfficacité
Si l'enregistrement dans un seau est trop grand(Actuellement, c'estTREEIFY_THRESHOLD = 8),HashMapVa utiliser dynamiquement untreemapImplémenter pour le remplacer.Ce serait mieux de le faire,- Oui.O(logn),Et pas malO(n).Comment ça marche?
Ceux qui sont en conflitKEYLes enregistrements correspondants sont simplement ajoutés à une liste liée,Ces enregistrements ne peuvent être trouvés que par traversée.Mais après avoir dépassé ce seuilHashMapCommencez à mettre à jour la liste en un arbre binaire,Utilisez la valeur de hachage comme variable de branche pour l'arbre,Si les deux valeurs de hachage ne sont pas égales,Mais si vous pointez vers le même seau,Le plus grand sera inséré dans le Sous - arbre droit.Si les valeurs de hachage sont égales,HashMapL'espoirkeyLes valeurs sont mieux réaliséesComparableInterface,Pour qu'il puisse être inséré dans l'ordre.Voilà.HashMapDekeyCe n'est pas nécessaire.,Mais si c'est fait, c'est mieux.Si cette interface n'est pas implémentée,En cas de grave collision de hachage,Vous ne pouvez pas vous attendre à une amélioration des performances.
À quoi sert cette amélioration des performances?Comme un programme malveillant,S'il sait qu'on utilise un algorithme de hachage,Il peut envoyer un grand nombre de demandes,Cause de graves collisions de hachage.Et j'ai continué à y accéderkeyPeut affecter considérablement les performances du serveur,Cela crée une attaque de déni de service(DoS).JDK 8DeO(n)ÀO(logn)Un bond en avant,Peut efficacement prévenir des attaques similaires,Et en même tempsHashMapLa prévisibilité des performances s'est légèrement améliorée
D'après ce qui précède:En expansionresize()En cours,Données sur l'ancien tableau Transfert à Sur un nouveau tableau,Opération de transfert de données = Traverser la liste de liens dans l'ordre positif de l'ancienne liste de liens、Insérer à tour de rôle dans la tête de la nouvelle liste,C'est - à - dire le transfert de données、Après expansion,Il est facile d'avoir une liste en ordre inverse
Régler pour ne pas changer après le recalcul de l'emplacement de stockage,C'est - à - dire avant l'expansion = 1->2->3,Après expansion = 3->2->1
À ce stade, si l'exécution simultanée put Fonctionnement,En cas d'expansion,Et Facile à voir Liste des anneaux,Pour obtenir des données、En traversant la liste Formation d'un cycle mort(Infinite Loop),C'est une impasse



getOrDefault() Méthode pour obtenir la désignation key La réponse value,Si vous ne trouvez pas key ,Renvoie la valeur par défaut définie.

Dans le cas d'un seul thread,rehashAucun problème.

Supposons ici que deux Threads exécutent simultanémentputL'opération a déclenchérehash,Mise en œuvretransferMéthodes,Et supposons qu'une fois le thread entrétransferMéthode et exécution terminéenext = e.nextAprès,Parce que le thread Schedule n'a plus de temps alloué“Pause”,Le thread 2 est terminétransferExécution de la méthode.L'état actuel est le suivant.

Puis le fil1Réveillée,Poursuivre le reste du premier cycle
Les résultats sont présentés ci - dessous.

Puis le prochain cycle,Le diagramme d'état des résultats est le suivant

Continuez le prochain cycle,Le diagramme d'état des résultats est le suivant

La liste des boucles se forme,Etkey(11)Impossible de rejoindre le thread1Un nouveau tableau de.Une boucle morte se produit la prochaine fois que vous accédez à la liste liée.
7 Fast-fail Cause de l'accidentLors de l'utilisation d'un Itérateur, siHashMapModifié,AlorsConcurrentModificationExceptionSera jeté,C'est - à - direFast-failStratégie.
QuandHashMapDeiterator()Quand la méthode est appelée,Va construire et retourner un nouveauEntryIteratorObjet,Et vaEntryIteratorDeexpectedModCountSet toHashMapDemodCount(Cette variable enregistreHashMapNombre de modifications).
En passant parIteratorDenextMéthode d'accès suivantEntryHeure,Il va d'abord vérifier sonexpectedModCountAvecHashMapDemodCountégal ou non,Si ce n'est pas égal,DescriptionHashMapModifié,Lancer directementConcurrentModificationException.LeIteratorDeremoveLa méthode fera un examen similaire.L'exception est lancée pour avertir l'utilisateur d'un problème de sécurité de fil tôt.
Solutions de sécurité pour les fils
Avec un seul thread,Pour éviterConcurrentModificationException,Il faut s'assurer que seuls lesHashMapEn soi ou seulement parIteratorPour modifier les données,Ça ne peut pas êtreIteratorUtiliser avant la fin de l'utilisationHashMapModifier les données par sa propre méthode.Parce que passerIteratorLors de la suppression des données,HashMapDemodCountEtIteratorDeexpectedModCountÇa va augmenter.,Sans préjudice de l'égalité des deux.S'il s'agit d'ajouter des données,Uniquement parHashMapLa méthode elle - même est terminée,Si vous voulez continuer à traverser les données à ce stade,Besoin de rappeleriterator()Pour reconstruire un nouveauIterator,Pour créer un nouveauIteratorDeexpectedModCountAvec mise à jourHashMapDemodCountÉquivalence.
Dans des conditions multithreadées,Disponible enCollections.synchronizedMapLa méthode construit une synchronisationMap,Ou directement en utilisant thread SafeConcurrentHashMap.
版权声明
本文为[JavaCheng...PréfaceMembres]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/09/20210914165729767p.html