Maison > Java > javaDidacticiel > le corps du texte

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

青灯夜游
Libérer: 2019-11-23 15:21:26
original
2683 Les gens l'ont consulté

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);
}
Copier après la connexion

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();
Copier après la connexion

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;
    }
Copier après la connexion

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 << 5) - i

四、如何重写hashCode()

本节先介绍重写hashCode()方法应该遵守的原则,再介绍通用的hashCode()重写方法。

1、重写hashcode()的原则

通过前面的描述我们知道,重写hashCode需要遵守以下原则:

(1)如果重写了equals()方法,检查条件“两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等”是否成立,如果不成立,则重写hashCode ()方法。

(2)hashCode()方法不能太过简单,否则哈希冲突过多。

(3)hashCode()方法不能太过复杂,否则计算复杂度过高,影响性能。

2、hashCode()重写方法

《Effective Java》中提出了一种简单通用的hashCode算法
A、初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;
B、选取equals方法中用于比较的所有域(之所以只选择equals()中使用的域,是为了保证上述原则的第1条),然后针对每个域的属性进行计算:

(1) 如果是boolean值,则计算f ? 1:0

(2) 如果是byte\char\short\int,则计算(int)f

(3) 如果是long值,则计算(int)(f ^ (f >>> 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;
	}
}
Copier après la connexion

推荐教程: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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal