La méthode équivalente et la méthode HashCode en Java sont dans l'objet, donc chaque objet a ces deux méthodes. Parfois, nous devons mettre en œuvre des besoins spécifiques et pouvons avoir à réécrire ces deux méthodes. Aujourd'hui, nous présenterons les fonctions de ces deux méthodes.
Les méthodes equals () et hashcode () sont utilisées pour comparer dans la même classe, en particulier lors du stockage du même objet de classe dans le conteneur, tel que SET, pour déterminer si l'objet placé est dupliqué.
Ici, nous devons d'abord comprendre un problème:
Deux objets avec equals () égaux, hashcode () doit être égal et deux objets avec equals () pas égaux, ne peuvent pas prouver que leur hashcode () n'est pas égal. En d'autres termes, pour deux objets dont la méthode equals () n'est pas égale, HashCode () peut être égal. (Ma compréhension est causée par les conflits de code de hachage lorsqu'il est généré)
Ici, HashCode est comme l'indice de chaque personnage dans le dictionnaire, et equals () est comme comparer différents mots sous le même personnage dans le dictionnaire. Tout comme dans le dictionnaire, à la recherche des deux mots "soi" et "spontané" sous le mot "soi" dans le dictionnaire, si equals () est utilisé pour déterminer l'égalité des mots requête, c'est le même mot. Par exemple, les deux mots comparés par equals () sont "auto", alors les valeurs obtenues par la méthode hashcode () doivent être égales à ce moment; Si la méthode equals () compare les mots "self" et "spontané", le résultat est que vous ne voulez pas attendre, mais ces deux mots appartiennent aux mots "soi" et donc lorsque vous recherchez des index, c'est-à-dire, HashCode () est le même. Si equals () compare les mots «self» et «ils», les résultats sont également différents, et les résultats obtenus par HashCode () sont également différents pour le moment.
Inversement: HashCode () est différent, et equals () peut être introduit; HashCode () est égal, égal () peut être égal ou peut ne pas être égal. Dans la classe d'objets, la méthode HashCode () est une méthode locale, qui renvoie la valeur d'adresse de l'objet. La méthode equals () dans la classe d'objets compare également les valeurs d'adresse des deux objets. Si equals () est égal, cela signifie que les valeurs d'adresse des deux objets sont également égales, bien sûr, HashCode () est égal;
Dans le même temps, l'algorithme de hachage offre une grande efficacité pour trouver des éléments
Si vous souhaitez savoir si un objet est contenu dans une collection, comment rédigez-vous le code du programme approximatif?
Vous retirez généralement chaque élément un par un pour comparer avec l'objet que vous recherchez. Lorsque vous constatez que le résultat de la comparaison de méthode égaux entre un élément et l'objet que vous recherchez, arrêtez de rechercher et renvoyez des informations positives. Sinon, renvoyez des informations négatives. S'il y a de nombreux éléments dans une collection, tels que 10 000 éléments et ne contiennent pas l'objet que vous recherchez, cela signifie que votre programme doit retirer 10 000 éléments de la collection et comparer un par un pour obtenir une conclusion.
Quelqu'un a inventé un algorithme de hachage pour améliorer l'efficacité de la recherche d'éléments d'un ensemble. De cette façon, l'ensemble est divisé en plusieurs zones de stockage. Chaque objet peut calculer un code de hachage et le code de hachage peut être regroupé (calculé à l'aide de différentes fonctions de hachage). Chaque groupe correspond à une certaine zone de stockage. Selon le hachage d'un objet, il peut déterminer dans quelle zone l'objet doit être stocké. HashSet utilise un algorithme de hachage pour accéder à l'ensemble des objets. Il utilise en interne la méthode pour prendre le reste d'un certain nombre n (cette fonction de hachage est la plus facile) pour regrouper et diviser les codes de hachage. La classe d'objet définit une méthode HashCode () pour renvoyer le code de hash de chaque objet Java. Lorsque vous recherchez un objet à partir d'une collection HashSet, le système Java appelle d'abord la méthode HashCode () de l'objet pour obtenir la table de code de hash de l'objet. , puis trouvez la zone de stockage correspondante en fonction du hachage, et enfin obtenez chaque élément dans la zone de stockage et comparez-le avec l'objet pour la méthode Equals; De cette façon, vous pouvez obtenir la conclusion sans traverser tous les éléments de la collection. On peut voir que la collection HashSet a de bonnes performances de récupération d'objets, mais l'efficacité du stockage d'objets dans la collection HashSet est relativement faible, car lors de l'ajout d'un objet à la collection HashSet, vous devez d'abord calculer le code de hash de l'objet et déterminer l'emplacement de stockage de l'objet dans la collection en fonction de ce code de hachage. Afin de s'assurer que les objets d'instance d'une classe peuvent être stockés normalement dans HashSet, les résultats des deux objets d'instance de cette classe doivent également être égaux lorsque les résultats comparés par la méthode equals () sont égaux; Autrement dit, si le résultat de l'OBJ1.Equals (OBJ2) est vrai, le résultat de l'expression suivante doit également être vrai:
obj1.hashcode () == obj2.hashcode ()
En d'autres termes: lorsque nous réécrivons la méthode égale d'un objet, nous devons réécrire sa méthode HashCode. Cependant, si nous ne réécrivons pas sa méthode HashCode, la méthode HashCode dans l'objet objet renvoie toujours l'adresse de hash d'un objet, et cette adresse n'est jamais égale. Ainsi, même si la méthode équivalente est réécrite pour le moment, il n'y aura pas d'effet spécifique, car si la méthode HashCode ne veut pas attendre, elle n'appellera pas la méthode égale de comparaison, donc elle n'a pas de sens.
Si la méthode HashCode () d'une classe ne suit pas les exigences ci-dessus, alors lorsque les résultats de la comparaison entre les deux objets d'instance de cette classe sont égaux à la méthode equals (), ils ne doivent pas être stockés dans l'ensemble ensemble en même temps. Cependant, s'ils sont stockés dans l'ensemble de hashset, car la valeur de retour de leur méthode HashCode () est différente (la valeur de retour de la méthode HashCode dans l'objet est toujours différente), le deuxième objet peut être placé dans un domaine différent du premier objet d'abord en fonction du calcul du code de hash, donc il ne peut pas si vous pouvez comparer la méthode Equals avec le premier objet, il peut être stocké dans la collection Hashset. La méthode HashCode () dans la classe d'objets ne peut pas répondre aux exigences de l'objet stocké dans le HashSet, car sa valeur de retour est calculée à partir de l'adresse de mémoire de l'objet. La valeur de hachage renvoyée par le même objet à tout moment pendant l'exécution du programme est toujours inchangée. Par conséquent, tant qu'il s'agit de deux objets d'instance différents, même si les résultats de comparaison de leur méthode égaux sont égaux, la valeur de retour de leur méthode HashCode par défaut est différente.
Jetons un coup d'œil à un exemple spécifique:
Objet rectObject: package com.weijia.demo; classe publique rectObject {public int x; public int y; public rectObject (int x, int y) {this.x = x; this.y = y; } @Override public int hashcode () {final int prime = 31; Int résultat = 1; Résultat = Prime * Résultat + x; Résultat = Prime * Résultat + Y; Résultat de retour; } @Override public boolean equals (objet obj) {if (this == obj) return true; if (obj == null) return false; if (getClass ()! = obj.getClass ()) return false; rectObject final autre = (rectObject) obj; if (x! = autre.x) {return false; } if (y! = autre.y) {return false; } return true; }} Nous avons remplacé le HashCode et égal aux méthodes dans l'objet de classe parent et voyons que dans les méthodes HashCode et égal, si les valeurs X et Y des deux objets RectObject sont égaux, leurs valeurs de code de hashs sont égales et égal aux renvoies True;
Voici le code de test:
Package com.weijia.demo; import java.util.hashset; classe publique Demo {public static void main (String [] args) {hashSet <cctObject> set = new HashSet <cctObject> (); Rectobject r1 = nouveau rectObject (3,3); Rectobject r2 = nouveau rectObject (5,5); Rectobject r3 = nouveau rectObject (3,3); set.add (R1); set.add (R2); set.add (R3); set.add (R1); System.out.println ("Size:" + set.size ()); }} Nous avons stocké quatre objets dans le hashset et imprimé la taille de la collection de jeux. Quel est le résultat?
Résultat en cours: Taille: 2
Pourquoi est-ce 2? Ceci est très simple, car nous réécrivons la méthode HashCode de la classe RectObject. Tant que les valeurs d'attribut x et y de l'objet rectObject sont égales, alors ses valeurs de code de hash sont également égales. Comparez donc d'abord les valeurs de code de hash. Les valeurs d'attribut x et y des objets R1 et R2 ne sont pas égales, donc leurs codes de hashs sont différents, donc l'objet R2 peut être mis en place, mais les valeurs d'attribut x et y de l'objet R3 sont les mêmes que les valeurs d'attribut de l'objet R1, donc le code de hash est égal. À l'heure actuelle, nous comparons la méthode égale de R1 et R3, car les valeurs x et y des deux sont égales, donc les objets R1 et R3 sont égaux, donc R3 ne peut pas être mis. De plus, l'ajout d'un R1 à la fin n'est pas ajouté, il n'y a donc que deux objets, R1 et R2 dans l'ensemble de jeu.
Ensuite, nous commençons la méthode HashCode dans l'objet rectObject, c'est-à-dire que nous ne remplacons pas la méthode HashCode dans l'objet objet et exécutons le code:
Résultat en cours: Taille: 3
Ce résultat est également très simple. Tout d'abord, jugez le code de hash de l'objet R1 et l'objet R2. Étant donné que la méthode HashCode dans l'objet renvoie le résultat de la conversion de l'adresse mémoire locale de l'objet, le code hashcode de différents objets d'instance est différent. De même, parce que le code de hash de R3 et R1 est également inégal, mais R1 == R1, donc dans l'ensemble final, il n'y a que trois objets R1, R2 et R3, donc la taille est 3
Ensuite, nous commençons le contenu de la méthode égaux dans l'objet rectObject, renvoyons directement false, sans commenter la méthode HashCode et exécuter le code:
Résultat en cours: Taille: 3
Ce résultat est un peu inattendu, analysons-le:
Tout d'abord, les objets de R1 et R2 comparent HashCode, qui ne sont pas égaux, donc R2 est mis dans l'ensemble, puis regardez les méthodes de code de hashcode de R3 et comparez R1 et R3, qui sont égales, puis comparent leurs méthodes égales. Parce que la méthode équivalente renvoie toujours fausse, R1 et R3 sont également inégaux, et il n'est pas nécessaire de mentionner R3 et R2. Les codes de hash de leurs deux ne sont pas égaux, donc R3 est mis dans l'ensemble, puis regardez R4, et comparez R1 et R4 constatent que les codes de hashs sont égaux. Lorsque vous comparez la méthode égaux, car les rendements égaux sont faux, R1 et R4 ne sont pas égaux, les mêmes R2 et R4 sont également inégaux, et R3 et R4 sont également inégaux, donc R4 peut être placé dans l'ensemble de jeu, donc le résultat devrait être de taille: 4, alors pourquoi est-ce 3?
À l'heure actuelle, nous devons vérifier le code source de Hashset. Voici le code source de la méthode ADD dans HashSet:
/ ** * Ajoute l'élément spécifié à cet ensemble s'il n'est pas déjà présent. * Plus formellement, ajoute l'élément spécifié <TT> E </TT> à cet ensemble si * cet ensemble ne contient aucun élément <TT> E2 </tt> tel que * <Tt> (e == null? E2 == null: e.equals (e2)) </tt>. * Si cet ensemble contient déjà l'élément, l'appel laisse l'ensemble * inchangé et renvoie <tt> false </tt>. * * @param e élément à ajouter à cet ensemble * @return <tt> true </ tt> Si cet ensemble ne contenait pas déjà l'élément * spécifié * / booléen public Add (e e) {return map.put (e, présent) == null; } Ici, nous pouvons voir que HashSet est réellement implémenté en fonction de HashMap. Nous cliquons sur la méthode de put de HashMap, le code source est le suivant:
/ ** * associe la valeur spécifiée à la clé spécifiée de cette carte. * Si la carte contenait auparavant un mappage pour la clé, l'ancienne valeur * est remplacée. * * @param clé de clé avec laquelle la valeur spécifiée doit être associée * @param valeur de valeur à association à la clé spécifiée * @return la valeur précédente associée à <tt> clé </ tt>, ou * <tt> null </tt> s'il n'y avait pas de mappage pour <TT> clé </ tt>. * (A <TT> NULL </TT> RETOUR peut également indiquer que la carte * précédemment associée <TT> NULL </TT> avec <TT> Key </ TT>.) * / Public v put (k key, v Value) {if (key == null) return putFornullKey (value); int hash = hash (key); int i = indexfor (hash, table.length); pour (entrée <k, v> e = table [i]; e! = null; e = e.next) {objet k; if (e.hash == hash && ((k = e.key) == key || key.equals (k))) {v oldValue = e.Value; e.Value = valeur; e.recordAccess (this); Retour OldValue; }} modCount ++; Addentry (hachage, clé, valeur, i); retourner null; } Examinons principalement les conditions de jugement de If.
Tout d'abord, nous déterminons si le code de hash est égal. S'il n'est pas égal, sautez-le directement. S'il est égal, comparez si ces deux objets sont égaux ou la méthode égale de ces deux objets. Parce qu'il est effectué ou opéré, tant que l'on est vrai, nous pouvons l'expliquer ici. En fait, la taille de l'ensemble ci-dessus est de 3, car le dernier R1 n'a pas été mis, et on pensait que R1 == R1 est revenu vrai, il n'a donc pas été mis. Donc, la taille de l'ensemble est 3. Si nous définissons la méthode de code de hash pour toujours renvoyer False, cet ensemble sera 4.
Enfin, jetons un coup d'œil à la fuite de mémoire causée par HashCode: Regardez le code:
Package com.weijia.demo; import java.util.hashset; classe publique Demo {public static void main (String [] args) {hashSet <cctObject> set = new HashSet <cctObject> (); Rectobject r1 = nouveau rectObject (3,3); Rectobject r2 = nouveau rectObject (5,5); Rectobject r3 = nouveau rectObject (3,3); set.add (R1); set.add (R2); set.add (R3); r3.y = 7; System.out.println ("Taille avant la suppression:" + set.size ()); set.Remove (R3); System.out.println ("Taille après la suppression:" + set.size ()); }} Résultats en cours:
Taille avant la suppression: 3
Taille supprimée: 3
Rush, j'ai trouvé un problème, et c'était un gros problème. Nous avons appelé Supprimer pour supprimer l'objet R3, pensant qu'il avait été supprimé, mais en fait, il n'a pas été supprimé. C'est ce qu'on appelle la fuite de mémoire, qui est un objet inutilisé, mais il est toujours en mémoire. Donc, après avoir opéré cela plusieurs fois, la mémoire explose. Jetez un œil au code source de Supprimer:
/ ** * Supprime l'élément spécifié de cet ensemble s'il est présent. * Plus formellement, supprime un élément <TT> E </TT> tel que * <Tt> (o == null? E == NULL: O.Equals (e)) </tt>, * Si cet ensemble contient un tel élément. Renvoie <Tt> true </tt> si * cet ensemble contenait l'élément (ou de manière équivalente, si cet ensemble * a changé à la suite de l'appel). (Cet ensemble ne contiendra pas l'élément * une fois que l'appel renvoie.) * * @Param o Objet à supprimer de cet ensemble, s'il est présent * @return <tt> true </tt> si l'ensemble contenait l'élément spécifié * / public booléen retire (objet o) {return map.remove (o) == présent; } Regardez ensuite le code source de la méthode de suppression:
/ ** * Supprime le mappage de la clé spécifiée de cette carte si elle est présente. * * @Param Key Key dont le mappage doit être supprimé de la carte * @return la valeur précédente associée à la clé <TT> </tt>, ou * <tt> null </tt> s'il n'y avait pas de mappage pour <TT> clé </ tt>. * (A <TT> NULL </TT> RETOUR peut également indiquer que la carte * précédemment associée <tt> null </tt> avec <Tt> touche </tt>.) * / Public v Suppor (clé d'objet) {Entry <k, v> e = devateSryforKey (key); return (e == null? null: e.Value); } Jetons un coup d'œil au code source de la méthode devoEntryForkey:
/ ** * Supprime et renvoie l'entrée associée à la clé spécifiée * dans le hashmap. Renvoie null si le hashmap ne contient aucun mappage * pour cette clé. * / Entrée finale <k, v> removeENTryforkey (clé d'objet) {int hash = (key == null)? 0: Hash (clé); int i = indexfor (hash, table.length); Entrée <k, v> prev = table [i]; Entrée <k, v> e = prev; while (e! = null) {entry <k, v> next = e.next; Objet K; if (e.hash == hash && ((k = e.key) == key || (key! = null && key.equals (k))))) {modCount ++; taille--; si (prev == e) table [i] = suivant; else prev.Next = Suivant; e.recordRemoval (this); retour e; } prev = e; e = suivant; } return e; } Nous voyons que lorsque vous appelez la méthode de suppression, nous utiliserons d'abord la valeur HashCode de l'objet pour trouver l'objet, puis la supprimerons. Ce problème est dû au fait que nous avons modifié la valeur de l'attribut y de l'objet R3. Et parce que la méthode HashCode de l'objet RectObject a une valeur y participant à l'opération, le code de hash de l'objet R3 a changé, donc R3 n'est pas trouvé dans la méthode de suppression, donc la suppression a échoué. Autrement dit, le code de hash de R3 a changé, mais l'emplacement qu'il stocke n'a pas été mis à jour et est toujours dans son emplacement d'origine, donc lorsque nous utilisons son nouveau HashCode pour le trouver, nous ne le trouverons certainement pas.
En fait, la méthode ci-dessus est très simple à implémenter: comme indiqué sur la figure:
Une table de hachage linéaire très simple, la fonction de hachage utilisée est mod, le code source est le suivant:
/ ** * Renvoie l'index du code de hachage h. * / static int indexfor (int h, int le long) {return h & (longueur-1); } Il s'agit en fait d'une opération de mod, mais ce type d'opération est plus efficace que le%.
1,2,3,4,5 signifie le résultat du mod, et chaque élément correspond à une structure de liste liée. Par conséquent, si vous souhaitez supprimer une entrée <k, v>, vous obtiendrez d'abord HashCode, afin d'obtenir le nœud d'en-tête de la liste liée, puis de parcourir la liste liée. Si HashCode et Equals sont égaux, supprimez cet élément.
La fuite de mémoire ci-dessus me indique un message: si nous participons à l'opération HashCode de la valeur d'attribut de l'objet, nous ne pouvons pas modifier sa valeur d'attribut lors de la suppression, sinon de graves problèmes se produiront.
En fait, nous pouvons également consulter la méthode HashCode et égal à la méthode des types d'objets correspondant aux 8 types de données de base.
Parmi eux, le code de hash du type de base en 8 est très simple pour retourner directement leur taille numérique. L'objet de chaîne se fait par une méthode de calcul complexe, mais cette méthode de calcul peut garantir que si les valeurs de cette chaîne sont égales, leur code de hash sera égal. Les 8 types de base de méthodes égaux sont de comparer directement les valeurs numériques, et la méthode Equals Type Equal compare les valeurs des chaînes.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.