ホームページ  >  記事  >  Java  >  Javaの==、equals、hashCodeの違いと関連性を詳しく説明したサンプルコード

Javaの==、equals、hashCodeの違いと関連性を詳しく説明したサンプルコード

黄舟
黄舟オリジナル
2017-03-14 11:25:011354ブラウズ


1. 概要

1. 概念

  • ==: 演算子 は、 オペランドの値間の関係を計算します。 equals:

  • オブジェクト
  • インスタンスメソッドは、2つのオブジェクトコンテンツが同じかどうかを比較します

    ハッシュ
  • コード:オブジェクトの
  • ネイティブ メソッド 、オブジェクトのハッシュ値を取得します。ハッシュテーブル内のオブジェクトのindex位置を決定するために使用されます。実際にはint型ですinteger 2. 関係演算子 = =

    1. オペランドの値

基本

データ型

変数
  • Javaには8つの基本データ型があります: 浮動小数点型:

    float

    (4 byte)、double(8) byte)

    整数型:byte(1バイト)、short(2バイト)、int(4バイト)、long(8バイト) 文字型:char(2バイト) ブール型:boolean(JVMの仕様占有する領域のサイズは明確に規定していませんが、リテラル値 "true" と "false" のみを取ることができると規定しているだけです)

    これら 8 つの基本データ型の変数の場合、変数は直接格納します「価値観」。したがって、関係演算子 == を使用して比較する場合、比較されるのは「値」そのものです。

    浮動小数点型と整数型はどちらも符号付き型であることに注意してください (最上位ビットは正と負を表すためにのみ使用され、計算には関与しません [バイトを例に取ると、その範囲は -2^ です) 7 ~ 2^ 7 - 1、-0 は -128])、char は unsigned 型です (計算にはすべてのビットが含まれるため、char 型の値の範囲は 0~2^16-1 です)

    Reference

    型変数
  • Javaでは、

参照型変数は「値」そのものを格納するのではなく、それに関連付けられたオブジェクトのアドレスをメモリ内に格納します
  • 。たとえば、次のコード行、

        String str1;

    この文は参照型変数を宣言していますが、現時点ではどのオブジェクトにも関連付けられていません。 そして、newを使ってオブジェクトを生成し、このオブジェクトをstr1にバインドします:
    str1= new String("hello");

    そして、str1はこのオブジェクトを指します。このとき、
  • 参照変数str1は、それが指すオブジェクトをメモリに格納します。 「値」自体、つまり、直接保存された
文字列

"hello"
ではありません。ここでの参照は、C/C++ のポインターに非常に似ています。

2. 概要

したがって、関係演算子 == の場合:

オペランドの型が

基本データ型

の場合、関係演算子は左オペランドと右オペランドの

    を決定します。値
  • が等しいかどうかオペランドの型が参照データ型の場合、関係演算子は左と右のオペランドの

  • メモリアドレス
  • が同じかどうかを判定します。 つまり、この時点で true が返された場合、オペレーターは同じオブジェクトに対して動作している必要があります。 3.equalsメソッド1. ソース equalsメソッドは基本クラスObjectのインスタンスメソッドなので、Objectから

    を継承するすべてのクラスがこのメソッドを持つことになります。
オブジェクト内の宣言:
    public boolean equals(Object obj) {}

2.equalsメソッドの役割


本来の目的: 2つのオブジェクトの
内容
が同じかどうかを判定する


equalsメソッドの役割をより直感的に理解するために, まず、
オブジェクトクラスのequalsメソッドの実装を見てみましょう。

  public boolean equals(Object obj) {    return (this == obj);
  }
明らかに、 Object クラスでは、2 つのオブジェクトの参照が等しいかどうか、つまり、同じオブジェクトを指しているかどうかを比較するために、equals メソッドが使用されます。

  但我们都知道,下面代码输出为 true:

public class Main {
    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = new String("hello");

        System.out.println(str1.equals(str2));
    }
}

原来是 String 类重写了 equals 方法:

public boolean equals(Object anObject) {   // 方法签名与 Object类 中的一致
    if (this == anObject) {     // 先判断引用是否相同(是否为同一对象),
        return true;
    }    if (anObject instanceof String) {   // 再判断类型是否一致,
        // 最后判断内容是否一致.
        String anotherString = (String)anObject;        
        int n = count;        
        if (n == anotherString.count) {        
        char v1[] = value;        
        char v2[] = anotherString.value;        
        int i = offset;        
        int j = anotherString.offset;       
         while (n-- != 0) {            
         if (v1[i++] != v2[j++])            
         return false;
        }        return true;
        }
    }    return false;
}

即对于诸如“字符串比较时用的什么方法,内部实现如何?”之类问题的回答即为:

使用equals方法,内部实现分为三个步骤:

  • 比较引用是否相同(是否为同一对象),

  • 判断类型是否一致(是否为同一类型),

  • 最后 比较内容是否一致

Java 中所有内置的类的 equals 方法的实现步骤均是如此,特别是诸如 Integer,Double 等包装器类。


3、equals 重写原则

对象内容的比较才是设计equals()的真正目的,Java语言对equals()的要求如下,这些要求是重写该方法时必须遵循的:

  • 对称性: 如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true” ;

  • 自反性: x.equals(x)必须返回是“true” ;

  • 类推性: 如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true” ;

  • 一致性: 如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true” ;

  • 对称性: 如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。

  • 任何情况下,x.equals(null)【应使用关系比较符 ==】,永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”


4、小结
 因此,对于 equals 方法:

  • 本意比较两个对象的 content 是否相同

  • 必要的时候,我们需要重写该方法,避免违背本意,且要遵循上述原则


四、hashCode 方法

1、hashCode 的来源
  hashCode 方法是基类Object中的 实例native方法,因此对所有继承于Object的类都会有该方法。
  
  在 Object类 中的声明(native方法暗示这些方法是有实现体的,但并不提供实现体,因为其实现体是由非java语言在外面实现的):

     public native int hashCode();

2、哈希相关概念
 我们首先来了解一下哈希表:

  • 概念 : Hash 就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出(int),该输出就是散列值。这种转换是一种  压缩映射,也就是说,散列值的空间通常远小于输入的空间。不同的输入可能会散列成相同的输出,从而不可能从散列值来唯一的确定输入值。简单的说,就是一种将任意长度的消息压缩到某一固定长度的消息摘要函数

  • 应用–数据结构 : 数组的特点是:寻址容易,插入和删除困难; 而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入和删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法——拉链法,我们可以理解为 “链表的数组”,如图:

             Javaの==、equals、hashCodeの違いと関連性を詳しく説明したサンプルコード
                               图1 哈希表示例

    左側は明らかに配列であり、配列の各メンバーはリンクされたリストです。このデータ構造が保持するすべての要素には、要素間をリンクするためのポインタが含まれています。要素をそれぞれの特性に基づいてさまざまなリンク リストに割り当て、これらの特性に基づいて正しいリンク リストを見つけ、リンク リストから要素を見つけます。 その中で、要素の特性に基づいて要素配列の添字を計算する方法がハッシュ法です。

  • ジッパーメソッドの適用範囲: 素早い検索と削除のための基本的なデータ構造。通常、データの総量はメモリに入れることができます。

  • キーポイント:
    ハッシュ関数の選択、文字列、整数、順列、特定の対応するハッシュメソッド用;
    衝突処理、1つはジッパーメソッドとも呼ばれるオープンハッシュ、もう1つはクローズドハッシュとも呼ばれます。オープンアドレッシング方式。


3. hashCodeの簡単な説明
Javaでは、Objectクラスで定義されたhashCodeメソッドは、オブジェクトごとに異なる整数を返します。 (これは、オブジェクトの内部アドレスを整数に変換することによって実現されますが、JavaTM プログラミング言語では、この実装トリックは必要ありません)。

hashCode の一般規約は次のとおりです:

  • Java アプリケーションの実行中、hashCode メソッドは、オブジェクトが等しい 使用される情報は変更されていません。この整数は、アプリケーションの 1 つの実行から同じアプリケーションの別の実行まで一貫している必要はありません。

  • equals(Object) メソッドに従って 2 つのオブジェクトが等しい場合、2 つのオブジェクトのそれぞれに対して hashCode メソッドを呼び出すと、同じ整数の結果が生成されなければなりません。

  • equals(java.lang.Object) メソッドに従って 2 つのオブジェクトが等しくない場合、2 つのオブジェクトのいずれかで hashCode メソッド を呼び出しても、異なる整数の結果を生成する は必要ありません。 。ただし、プログラマは、等しくないオブジェクトに対して異なる整数の結果を生成すると、ハッシュ テーブルのパフォーマンスが向上する可能性があることに注意する必要があります。

    hashCodeの役割をさらに理解するには、まずJavaのコンテナを理解する必要があります。HashCodeは、HashSet、HashMap、Hashtableなどのハッシュアルゴリズムを必要とするデータ構造でのみ有用だからです。

    JavaのコレクションにはList、Queue、Setの3種類があります。 最初の 2 つのセットの要素は順序付けされており、要素を繰り返すことができますが、最後のセットの要素は順序付けされていませんが、要素を繰り返すことはできません。

    さて、ここで重大な問題があります。要素が繰り返されないようにしたい場合、2 つの要素が繰り返されているかどうかを判断する基準は何でしょうか? これは Object.equals メソッドです。ただし、要素を追加するたびに一度チェックすると、要素数が多い場合、コレクションに追加された要素の比較回数が非常に多くなります。 つまり、コレクション内に 1000 個の要素がある場合、最初の 001 要素がコレクションに追加されると、equals メソッドが 1000 回呼び出されます。これでは明らかに効率が大幅に低下します。したがって、Java はハッシュテーブルの原則を採用しました。 このように、ハッシュ アルゴリズムを使用して、コレクションに格納される各要素の値を計算し、この値に基づいて配列内の要素の位置を計算します。したがって、新しい要素がコレクションに追加されるときは、次の 2 つのステップに分けることができます:

  • まず、この要素の hashCode メソッドを呼び出し、次に、取得された値に基づいて配列内の要素の位置を計算します。価値。この位置に要素がない場合は、この位置に直接保存します。

  • この位置に要素がすでにある場合は、そのequalsメソッドを呼び出して、新しい要素と比較します。それ以外の場合は、この位置に対応するリンク リストに格納されます (Java での HashSet、HashMap、および Hashtable の実装では、要素は常にリンク リストの先頭に配置されます)。


4.equalsとhashCode

前提: hashCodeに関しては、両方ともObjectクラスのメソッドであるequalsメソッドについて話さなければなりません。 Object クラスはすべてのクラスの基本クラスであるため、これら 2 つのメソッドはすべてのクラスでオーバーライドできます。

  • 原則 1: x.equals(y) が「true」を返す場合、x と y の hashCode() は等しくなければなりません

  • 原則 2:

    x.equals(y) の場合" false" を返す場合、x と y の hashCode() は等しいか異なる可能性があります

  • 原则 3 : 如果 x 和 y 的 hashCode() 不相等,那么 x.equals(y) 一定返回 “false” ;

  • 原则 4 : 一般来讲,equals 这个方法是给用户调用的,而 hashcode 方法一般用户不会去调用 ;

  • 原则 5 : 当一个对象类型作为集合对象的元素时,那么这个对象应该拥有自己的equals()和hashCode()设计,而且要遵守前面所说的几个原则。


5、实现例证

 hashCode()在object类中定义如下:

public native int hashCode();

 说明是一个本地方法,它的实现是根据本地机器相关的。

 String 类是这样重写它的:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence{
    /** The value is used for character storage. */
    private final char value[];     //成员变量1

    /** The offset is the first index of the storage that is used. */
    private final int offset;      //成员变量2

    /** The count is the number of characters in the String. */
    private final int count;       //成员变量3

   /** Cache the hash code for the string */
    private int hash; // Default to 0    //非成员变量

    public int hashCode() {    int h = hash;        int len = count;         //用到成员变量3
    if (h == 0 && len > 0) {        int off = offset;         //用到成员变量2
        char val[] = value;       //用到成员变量1
            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];       //递推公式
            }
            hash = h;
        }        return h;
    }
}

对程序的解释:h =  s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],由此可以看出,对象的hash地址不一定是实际的内存地址。


五、小结

  • hashcode是系统用来快速检索对象而使用

  • equals方法本意是用来判断引用的对象是否一致

  • 重写equals方法和hashcode方法时,equals方法中用到的成员变量也必定会在hashcode方法中用到,只不过前者作为比较项,后者作为生成摘要的信息项,本质上所用到的数据是一样的,从而保证二者的一致性


以上がJavaの==、equals、hashCodeの違いと関連性を詳しく説明したサンプルコードの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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