11

どちらかが等しい場合、このタイプのオブジェクトは等しいと見なされるように、2つのフィールドに基づいて等しいクラスがあります。このようなequals()のhashCode()関数を記述して、equalsがtrueを返すときにhashCodeが等しいという一般的なコントラクトが保持されるようにするにはどうすればよいですか?

public class MyClass {
  int id;
  String name;

  public boolean equals(Object o) {
    if (!(o instanceof MyClass))
      return false;
    MyClass other = (MyClass) o;
    if (other.id == this.id || other.name == this.name)
      return true;
    return false;
  }
}

このクラスのhashCode()関数を作成するにはどうすればよいですか?そして、私はここで次のような定数を返すという些細なケースを避けたいと思います。

public int hashCode() {
  return 1;
}
4

10 に答える 10

24

自明でないハッシュコードは存在しないと思います。また、API に記載されているequals()一般契約に違反しています---推移的ではありません:

(1,2)等しい(1,3)

(4,3)等しい(1,3)

しかし、と等しく(4,3)ありません。(1,2)


完全を期すために、スキート-ニコ証明 =)を紹介します。

Claim :ハッシュコードは自明な定数関数でなければなりません.

証明:(a,b)(c,d)を異なるハッシュコードを持つ 2 つのオブジェクトとしますh(a,b) ≠ h(c,d)。オブジェクトを考えてみましょう(a,d)。OP の定義により、(a,d)は と等しく(a,b)(a,d)は と等しくなり(c,d)ます。ハッシュコードのコントラクトから次のことがわかりますh(a,d) = h(a,b) = h(c,d)。矛盾。

于 2009-01-26T08:33:12.487 に答える
9

さて、あなたのシナリオでは、API要件を1秒間無視すると、非定数のハッシュ関数はありません。

の値が異なるハッシュ関数があったと想像してください。

(a、b)、(a、c)、b!= c、次にhash(a、b)!= hash(a、c)、ただし(a、b)=(a、c)。

同様に、(b、a)と(c、a)は同じhashCodeを発行する必要があります。

ハッシュ関数をhと呼びましょう。我々は気づく:

h(x、y)= h(x、w)= h(v、w)forall x、y、v、w。

したがって、必要なことを実行する唯一のhashFunctionは定数です。

于 2009-01-26T08:56:09.570 に答える
6

私はザックが正しいと確信しています - これを行うための重要なハッシュコードはありません。

疑似証明:

X=(id1, name1) と Y=(id2, name2) の 2 つの等しくない値を考えてみましょう。

ここで、Z=(id2,name1) を考えてみましょう。これは X と Y の両方に等しいため、X と Y の両方と同じハッシュコードを持つ必要があります。したがって、X と Y は同じハッシュコードを持つ必要があります。つまり、すべての値が同じハッシュコードを持つ必要があります。

あなたが奇妙な状況に陥ったのには理由があります.equalsの推移的な性質を破っています. X.equals(Z) と Z.equals(Y)は X.equals(Y) を意味するはずですが、そうではありません。あなたの平等の定義は、通常の平等の契約には適していません。

于 2009-01-26T08:39:25.360 に答える
2

IDが等しいか名前が等しい場合のように意図的に同等性を定義しましたか?「OR」を「AND」にするべきではありませんか?

「AND」を意味する場合、ハッシュコードは、equals()で使用されているフィールドとまったく同じかそれ以下(ただし、equalsで使用されていないフィールドは使用しないでください)を使用して計算する必要があります。

「OR」を意味する場合は、hashgcodeのハッシュコード計算にidまたはnameを含めないでください。これは実際には意味がありません。

于 2009-01-26T10:26:22.227 に答える
2

できないと思います。その理由は、equals()メソッドが推移的ではないためです。

推移性とは、null でない 3 つの x、y、z、if x.equals(y)y.equals(z)、thenのことx.equals(z)です。x={id: 1, name: "ha"}あなたの例では、オブジェクトはこのプロパティを持っていy={id: 1, name: "foo"}ます。しかし、です。すべてのメソッドにこのプロパティが必要です。Java API ドキュメントを参照してください。z={id: 2, name: "bar"}(x.equals(y) and y.equals(z))x.equals(z)falseequals()

ハッシュ関数に戻る: すべての関数は、 によって定義された等価性を生成しf(x)==f(y)ます。つまり、関数値の比較に興味があり、true を返す場合x==y(および場合によっては他の場合)、推移的な関係を受け取ることを意味します。つまり、少なくともオブジェクトの等価性の推移的な閉鎖を考慮する必要があります。 . あなたの場合、推移閉包は自明な関係です(すべてが何かに等しい)。つまり、関数によって異なるオブジェクトを区別することはできません。

于 2009-01-26T08:39:53.253 に答える
0

編集:私は質問を注意深く読んでいませんでした。

-

commons-langjarを使用します。

メンバーhashCodeが機能するXOR。hashCode()とequals()を正しく実装する必要があるため。

ただし、hashCodeを保護しないと、コードが間違っている可能性があります。ハッシュ化されたら、変更しないでください。それが起こらないようにする必要があります。

public hashCode(){
   return new AssertionError();
}

また

 public class MyClass {
   final int id;
   final String name;
   // constructor
 }

また

public class MyClass {
   private int id;
   private String name;
   boolean hashed=false;
   public void setId(int value){
     if(hashed)throw new IllegalStateException();
     this.id=value;
   }
   public void setName(String value){
     if(hashed)throw new IllegalStateException();
     this.name=value;
   }
   // your equals() here
   public hashCode(){
     hashed=true;
     return new HashCodeBuilder().append(id).append(name).toHashCode();
   }
}
于 2009-01-26T09:10:59.837 に答える
0

質問を読み直した後。

いずれかのフィールドが更新されると、別のフィールドをオートコンプリートできます。

-

編集:私のコードは私の英語よりもよく言うかもしれません。

void setName(String value){
  this.id=Lookup.IDbyName(value);
}
void setID(String value){
  this.name=Lookup.NamebyId(value);
}

編集2:

IDと名前の両方を設定しない限り、常にtrueが返されるため、質問のコードが間違っている可能性があります。

部分的な等式を実行するメソッドが本当に必要な場合は、「partialEquals()」という名前の独自のAPIを作成してください。

于 2009-01-26T09:18:06.687 に答える
-1

最も簡単なルートは、個々のフィールドのハッシュコードをXORすることです。これは、状況によっては少し醜くなります(たとえば、X、Y座標では、XとYを反転すると、ハッシュが等しくなるという状況が悪化する可能性があります)が、全体的にはかなり効果的です。効率を上げるために必要に応じて、衝突を減らすために必要に応じて微調整します。

于 2009-01-26T08:24:45.610 に答える
-1

これはどう

public override int GetHashCode()
{
    return (id.ToString() + name.ToString()).GetHashCode();
}

関数は常に「有効な」ハッシュを返す必要があります...

編集:「and」ではなく「or」を使用していることに気付きました:Pまあ、この問題に対する良い解決策があるとは思えません...

于 2009-01-26T08:37:33.367 に答える
-2

どうですか

public override int GetHashCode()
{
    return id.GetHashCode() ^ name.GetHashCode();
}
于 2009-01-26T08:25:23.893 に答える