Maison  >  Article  >  Java  >  Méthode equals() et méthode hashCode() (introduction détaillée)

Méthode equals() et méthode hashCode() (introduction détaillée)

青灯夜游
青灯夜游original
2019-11-23 15:18:372613parcourir

La classe de base de Java Object fournit quelques méthodes, parmi lesquelles la méthode equals() est utilisée pour déterminer si deux objets sont égaux, et la méthode hashCode() est utilisée pour calculer le code de hachage de l'objet. Ni equals() ni hashCode() ne sont des méthodes finales et peuvent être écrasées.

Méthode equals() et méthode hashCode() (introduction détaillée)

Cet article présente certains problèmes qui nécessitent une attention particulière lors de l'utilisation et de la réécriture des deux méthodes.

1. Méthode égal()

La méthode égal(e) dans la classe Object est implémentée comme suit :

public boolean equals(Object obj) {
    return (this == obj);
}

Il ressort de cette implémentation que l'implémentation de la classe Object adopte l'algorithme avec le plus haut degré de différenciation, c'est-à-dire que tant que les deux objets ne sont pas le même objet, alors equals() doit renvoyer false .

Bien que nous puissions remplacer la méthode equals() lors de la définition d'une classe, il y a quelques précautions ; le JDK explique les conventions qui doivent être suivies lors de l'implémentation de la méthode equals() :

(1) Réflexivité : x.equals(x) doit renvoyer true.

(2) Symétrie : Les valeurs de retour de x.equals(y) et y.equals(x) doivent être égales.

(3) Transitivité : x.equals(y) est vrai et y.equals(z) est également vrai, alors x.equals(z) doit être vrai.

(4) Cohérence : Si les informations utilisées par les objets x et y dans equals() n'ont pas changé, alors la valeur de x.equals(y) restera toujours inchangée.

(5) Non nul : x n'est pas nul et y est nul, alors x.equals(y) doit être faux.

2. Méthode hashCode()

1. >La méthode hashCode() dans la classe Object est déclarée comme suit :

public native int hashCode();

On peut voir que hashCode() est une méthode native, et le type de valeur de retour est en fait un entier ; cette méthode native convertit l'objet. L'adresse en mémoire est renvoyée sous forme de code de hachage, ce qui garantit que la valeur de retour est différente pour différents objets.

Semblable à la méthode equals(), la méthode hashCode() peut être remplacée. Le JDK explique la fonction de la méthode hashCode() et les précautions lors de son implémentation :

(1) hashCode() fonctionne dans une table de hachage, telle que java.util.HashMap.

(2) Si les informations utilisées par l'objet dans equals() n'ont pas changé, alors la valeur hashCode() restera toujours inchangée.

(3) Si deux objets sont jugés égaux à l'aide de la méthode equals(), la méthode hashCode() doit également être égale.

(4) Si deux objets sont jugés inégaux à l'aide de la méthode equals(), hashCode() n'est pas requis et doit être inégal, cependant, les développeurs doivent se rendre compte que des objets inégaux produisent un hashCode différent. Peut améliorer les performances ; de tables de hachage.

2. Le rôle de hashCode()

En général, hashCode() fonctionne dans les tables de hachage, telles que HashSet, HashMap etc.

Lorsque nous ajoutons un objet à une table de hachage (comme HashSet, HashMap, etc.), nous appelons d'abord la méthode hashCode() pour calculer le code de hachage de l'objet, grâce au code de hachage, nous pouvons. localiser directement l'objet dans la table de hachage. La position dans (généralement le module du code de hachage à la taille de la table de hachage). S'il n'y a aucun objet à cette position, vous pouvez directement insérer l'objet à cette position ; s'il y a des objets à cette position (il peut y en avoir plusieurs, implémentés via une liste chaînée), appelez la méthode equals() pour comparer si ces objets sont égaux à l'objet. S'ils sont égaux, pas besoin de sauvegarder l'objet ; s'ils ne sont pas égaux, ajoutez l'objet à la liste chaînée.

Cela explique également pourquoi

equals() est égal, alors hashCode() doit être égal.

Si deux objets égal() sont égaux, ils ne doivent apparaître qu'une seule fois dans la table de hachage (comme HashSet, HashMap, etc.) ; si hashCode() n'est pas égal, alors ils seront hachés à différents emplacements dans le Table de hachage Position, apparaît plus d'une fois dans la table de hachage.

En fait, dans la JVM, l'objet chargé comprend trois parties dans la mémoire : l'en-tête de l'objet, les données d'instance et le remplissage. Parmi eux, l'en-tête de l'objet comprend un pointeur vers le type de l'objet et MarkWord, et MarkWord contient non seulement les informations sur l'âge de génération GC et les informations sur l'état de verrouillage de l'objet, mais inclut également le hashcode de l'objet

; les données de l'instance d'objet sont les informations valides réellement stockées par l'objet ; la partie de remplissage sert uniquement d'espace réservé car HotSpot exige que l'adresse de départ de l'objet soit un multiple entier de 8 octets.

3. Implémentation de equals() et hashCode() dans String

Le code d'implémentation pertinent dans la classe String est le suivant :
    private final char value[];
    private int hash; // Default to 0
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

Les points suivants peuvent être vus dans le code :

1 Les données String sont définitives, c'est-à-dire qu'une fois qu'un objet String est créé, il ne peut pas être modifié sous la forme de String s ; = "hello"; s = "world "; lorsque s = "world" est exécuté, ce n'est pas que la valeur de l'objet string devient "world", mais un nouvel objet String est créé et la référence s pointe vers le nouvel objet.

2. La classe String met en cache le résultat de hashCode() en tant que valeur de hachage pour améliorer les performances.

3. La condition pour que l'objet String égal() soit égal est que les deux soient des objets String, qu'ils aient la même longueur et qu'ils aient exactement la même valeur de chaîne ;

4. La formule de calcul hashCode() de String est : s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[ n -1]

Quant à la raison pour laquelle le nombre 31 est utilisé dans le processus de calcul de hashCode(), les principales raisons sont les suivantes :

1、使用质数计算哈希码,由于质数的特性,它与其他数字相乘之后,计算结果唯一的概率更大,哈希冲突的概率更小。

2、使用的质数越大,哈希冲突的概率越小,但是计算的速度也越慢;31是哈希冲突和性能的折中,实际上是实验观测的结果。

3、JVM会自动对31进行优化:31 * i == (i 77c98ee81678acf12c851e5747f5e864>> 32))

 (4) 如果是float值,则计算Float.floatToIntBits(f)

 (5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int

 (6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0

 (7) 如果是数组,那么需要为每个元素当做单独的域来处理。java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上。 

C、最后,把每个域的散列码合并到对象的哈希码中。

下面通过一个例子进行说明。在该例中,Person类重写了equals()方法和hashCode()方法。因为equals()方法中只使用了name域和age域,所以hashCode()方法中,也只计算name域和age域。

对于String类型的name域,直接使用了String的hashCode()方法;对于int类型的age域,直接用其值作为该域的hash。

public class Person {
	private String name;
	private int age;
	private boolean gender;

	public Person() {
		super();
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public boolean isGender() {
		return gender;
	}
	public void setGender(boolean gender) {
		this.gender = gender;
	}

	@Override
	public boolean equals(Object another) {
		if (this == another) {
			return true;
		}
		if (another instanceof Person) {
			Person anotherPerson = (Person) another;
			if (this.getName().equals(anotherPerson.getName()) && this.getAge() == anotherPerson.getAge()) {
				return true;
			} else {
				return false;
			}
		}
		return false;
	}

	@Override
	public int hashCode() {
		int hash = 17;
		hash = hash * 31 + getName().hashCode();
		hash = hash * 31 + getAge();
		return hash;
	}
}

推荐教程:java教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn