6

私はJava 7を使用しており、以下のクラスがあります。私は正しく実装equalsしましたが、問題は、以下のメイン メソッドで戻りますが、両方のオブジェクトに対して同じハッシュ コードを返すことです。ここで何か間違ったことをしていないかどうかを確認するために、このクラスをもっと多くの人に見てもらうことはできますか?hashCodeequalsfalsehashCode

更新:Objects.hashメソッドを呼び出す行を独自のハッシュ関数に置き換えました: chamorro.hashCode() + english.hashCode() + notes.hashCode()。異なるハッシュ コードを返します。これはhashCode、2 つのオブジェクトが異なる場合に行うべきことです。Objects.hashメソッドが壊れていませんか?

あなたの助けは大歓迎です!

import org.apache.commons.lang3.StringEscapeUtils;

public class ChamorroEntry {

  private String chamorro, english, notes;

  public ChamorroEntry(String chamorro, String english, String notes) {
    this.chamorro = StringEscapeUtils.unescapeHtml4(chamorro.trim());
    this.english = StringEscapeUtils.unescapeHtml4(english.trim());
    this.notes = notes.trim();
  }

  @Override
  public boolean equals(Object object) {
    if (!(object instanceof ChamorroEntry)) {
      return false;
    }
    if (this == object) {
      return true;
    }
    ChamorroEntry entry = (ChamorroEntry) object;
    return chamorro.equals(entry.chamorro) && english.equals(entry.english)
        && notes.equals(entry.notes);
  }

  @Override
  public int hashCode() {
    return java.util.Objects.hash(chamorro, english, notes);
  }

  public static void main(String... args) {
    ChamorroEntry entry1 = new ChamorroEntry("Åguigan", "Second island south of Saipan. Åguihan.", "");
    ChamorroEntry entry2 = new ChamorroEntry("Åguihan", "Second island south of Saipan. Åguigan.", "");
    System.err.println(entry1.equals(entry2)); // returns false
    System.err.println(entry1.hashCode() + "\n" + entry2.hashCode()); // returns same hash code!
  }
}
4

4 に答える 4

7

等しくないオブジェクトには異なる hashCode が必要であるという要件はありません。等しいオブジェクトは等しい hashCode を持つことが期待されますが、ハッシュの衝突は禁止されていません。 return 1; あまり有用ではないにしても、hashCode の完全に合法的な実装になります。

可能なハッシュ コードは 32 ビットしかなく、可能なオブジェクトの数は無制限です :) 衝突は時々発生します。

于 2012-12-09T06:54:25.287 に答える
4

HashCode は 32 ビットの int 値であるため、衝突 (2 つのオブジェクトの同じハッシュ コード) の可能性は常にありますが、まれに/偶然に発生します。あなたの例は、非常に偶然の一致の 1 つです。これが説明です。

を呼び出すとObjects.hash、次のようなロジックで内部的に呼び出さArrays.hashCode()れます。

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;
    int result = 1;
    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());
    return result;
}

3 パラメータ hashCode の場合、結果は次のようになります。

   31 * (31 * (31 *1 +hashOfString1)+hashOfString2) + hashOfString3

あなたの最初のオブジェクトのために。個々の文字列のハッシュ値は次のとおりです。

チャモロ --> 1140493257 英語 --> 1698758127 メモ --> 0

2 番目のオブジェクトの場合:

チャモロ --> 1140494218 英語 --> 1698728336 メモ -->0

お気付きのように、両方のオブジェクトのハッシュ コードの最初の 2 つの値が異なります。

しかし、最終的なハッシュコードを次のように計算すると:

  int hashCode1 = 31*(31*(31+1140493257) + 1698758127)+0;
  int hashCode2 = 31*(31*(31+1140494218) + 1698728336)+0;

偶然にも32 ビットで格納される1919283673ため、同じハッシュ コードになります。int

以下のコード セグメントを使用して、自分自身の理論を検証します。

  public static void main(String... args) {
    ChamorroEntry entry1 = new ChamorroEntry("Åguigan", 
                         "Second island south of Saipan. Åguihan.", "");
    ChamorroEntry entry2 = new ChamorroEntry("Åguihan", 
                         "Second island south of Saipan. Åguigan.", "");
    System.out.println(entry1.equals(entry2)); // returns false
    System.out.println("Åguigan".hashCode());
    System.out.println("Åguihan".hashCode());
    System.out.println("Second island south of Saipan. Åguihan.".hashCode());
    System.out.println("Second island south of Saipan. Åguigan.".hashCode());
    System.out.println("".hashCode());
    System.out.println("".hashCode());
    int hashCode1 = 31*(31*(31+1140493257) + 1698758127)+0;
    int hashCode2 = 31*(31*(31+1140494218) + 1698728336)+0;
    System.out.println(entry1.hashCode() + "\n" + entry2.hashCode()); 
    System.out.println(getHashCode(
                    new String[]{entry1.chamorro, entry1.english, entry1.notes}) 
                    + "\n" + getHashCode(
                    new String[]{entry2.chamorro, entry2.english, entry2.notes})); 
    System.out.println(hashCode1 + "\n" + hashCode2); // returns same hash code!
  }

    public static int getHashCode(Object a[]) {
        if (a == null)
            return 0;
        int result = 1;
        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());
        return result;
    }

いくつかの異なる文字列パラメーターを使用する場合は、異なる hashCode になることを願っています。

于 2012-12-09T07:37:33.337 に答える
2

2 つの等しくないオブジェクトが異なるハッシュを持つ必要はありません。重要なことは、2 つの等しいオブジェクトに対して同じハッシュを持つことです。

次のように hashCode() を実装できます。

public int hashCode() {
    return 5;
}

そしてそれは正しいままです(しかし非効率的です)。

于 2012-12-09T06:55:41.513 に答える