ホームページ  >  記事  >  Java  >  equals() メソッドと hashCode() メソッド (詳細な紹介)

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

青灯夜游
青灯夜游オリジナル
2019-11-23 15:18:372658ブラウズ

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 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教程

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

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。