ホームページ > Java > &#&チュートリアル > equals() メソッドと hashCode() メソッド (詳細な紹介)

equals() メソッドと hashCode() メソッド (詳細な紹介)

青灯夜游
リリース: 2019-11-23 15:21:26
オリジナル
2726 人が閲覧しました

Java の基本クラス Object はいくつかのメソッドを提供します。その中で、equals() メソッドは 2 つのオブジェクトが等しいかどうかを判断するために使用され、hashCode() メソッドはオブジェクトのハッシュ コードを計算するために使用されます。 equals() も hashCode() も最終メソッドではないため、上書きできます。

equals() メソッドと hashCode() メソッド (詳細な紹介)

#この記事では、2 つのメソッドを使用および書き換える際に注意が必要な問題をいくつか紹介します。

1.equal() メソッド

Object クラスのquals() メソッドは次のように実装されます:

public boolean equals(Object obj) {
    return (this == obj);
}
ログイン後にコピー

この実装から、Object クラスの実装では最も高度な差別化を伴うアルゴリズムが採用されていることがわかります。つまり、2 つのオブジェクトが同じオブジェクトでない限り、equals() は false を返さなければなりません。 。

#クラスを定義するときに、equals() メソッドをオーバーライドできますが、いくつかの注意事項があります。JDK では、equals() メソッドを実装するときに従う必要がある規則について説明しています。 # (1) 再帰性: x.equals(x) は true を返さなければなりません。

(2) 対称性: x.equals(y) と y.equals(x) の戻り値は等しくなければなりません。

(3) 推移性: x.equals(y) が true、y.equals(z) も true の場合、x.equals(z) は true でなければなりません。

(4) 一貫性:equals() 内のオブジェクト x および y によって使用される情報が変更されていない場合、x.equals(y) の値は常に変更されません。

(5) 非 null: x が null でなく、y が null の場合、x.equals(y) は false でなければなりません。

#2. hashCode() メソッド

1. オブジェクト hashCode()

#Object クラスの hashCode() メソッドは次のように宣言されています:

public native int hashCode();
ログイン後にコピー
hashCode() がネイティブ メソッドであり、戻り値の型が整数であることがわかります。ネイティブ メソッドはオブジェクトを次のように変換します。メモリ内のアドレスはハッシュ コードとして返されるため、戻り値はオブジェクトごとに異なります。

equals() メソッドと同様に、hashCode() メソッドもオーバーライドできます。 JDK では、hashCode() メソッドの機能と実装時の注意点を説明します。

(1) hashCode() は、java.util.HashMap などのハッシュ テーブルで動作します。

(2) オブジェクトがequals()で使用する情報が変更されていない場合、hashCode()の値は常に変更されません。

(3) equals() メソッドを使用して 2 つのオブジェクトが等しいと判断された場合、hashCode() メソッドも等しい必要があります。

(4) equals() メソッドを使用して 2 つのオブジェクトが等しくないと判断された場合、hashCode() は必要なく、等しくなければなりません。ただし、開発者は、等しくないオブジェクトは異なる hashCode を生成することを認識する必要があります。パフォーマンスを向上させることができます。ハッシュテーブルの。

2. hashCode() の役割

一般に、hashCode() は、HashSet、HashMap などのハッシュ テーブルで機能します。 オブジェクトをハッシュ テーブル (HashSet、HashMap など) に追加するとき、最初に hashCode() メソッドを呼び出して、オブジェクトのハッシュ コードを計算します。ハッシュ コードを通じて、次のことができます。ハッシュ テーブル内のオブジェクトを直接見つけます (通常、ハッシュ テーブルのサイズに対するハッシュ コードの係数)。この位置にオブジェクトがない場合は、この位置にオブジェクトを直接挿入できます。この位置にオブジェクトがある場合 (リンク リストを通じて実装され、複数ある場合があります)、equals() メソッドを呼び出して、次のいずれかを比較します。これらのオブジェクトはオブジェクトと等しいです。等しい場合はオブジェクトを保存する必要はありません。等しくない場合は、オブジェクトをリンク リストに追加します。

これは、

equals() が等しい場合、hashCode() が等しくなければならない理由も説明しています。

2 つのオブジェクトquals() が等しい場合、それらはハッシュ テーブル (HashSet、HashMap など) に 1 回だけ出現する必要があります。hashCode() が等しくない場合、それらはハッシュ テーブル内の異なる場所にハッシュされます。ハッシュ テーブル。ハッシュ テーブル内で複数回出現する位置。

実際には、JVM では、ロードされたオブジェクトには、オブジェクト ヘッダー、インスタンス データ、および埋め込みという 3 つの部分がメモリ内に含まれています。このうち、オブジェクト ヘッダーにはオブジェクトの型へのポインタと MarkWord が含まれており、

MarkWord にはオブジェクトの GC 世代情報とロック ステータス情報だけでなく、オブジェクトのハッシュコード 、オブジェクト インスタンスも含まれています。データはオブジェクトによって実際に格納されている有効な情報です。HotSpot ではオブジェクトの開始アドレスが 8 バイトの整数倍である必要があるため、パディング部分はプレースホルダーとしてのみ機能します。

3. String でのquals() と hashCode() の実装

String クラスの関連する実装コードは次のとおりです。 ##
    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;
    }
ログイン後にコピー
コードから次の点がわかります:
1. 文字列データは最終的なものです、つまり、 String オブジェクトが作成されると、それは String の形式で変更できません。 = "hello"; s = "world "; ステートメント、 s = "world" が実行されると、文字列オブジェクトの値が "world" に変更されるのではなく、新しい String オブジェクトが作成され、 s の参照が新しいオブジェクトを指します。

2. String クラスは、パフォーマンスを向上させるために hashCode() の結果をハッシュ値としてキャッシュします。

3. String オブジェクトquals() が等しい条件は、両方が String オブジェクトであり、長さが同じで、文字列値がまったく同じであることですが、同じオブジェクトである必要はありません。

4. String の hashCode() の計算式は、 s[0]*31^(n-1) s[1]*31^(n-2) ... s[n-1 ]

hashCode()の計算処理でなぜ31という数字が使われるのかについてですが、主な理由は以下のとおりです。

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;
	}
}
ログイン後にコピー

推荐教程:java教程

以上がequals() メソッドと hashCode() メソッド (詳細な紹介)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート