9

equals(Object)現在、次のようなオーバーライドがあります。

@Override
public boolean equals(Object o) {
    if (o == this) return true;
    if (! (o instanceof Player)) return false;
    Player p = (Player) o;
    return getFirstName().equalsIgnoreCase(p.getFirstName()) && 
            getLastName().equalsIgnoreCase(p.getLastName());
}

hashCode()は現在次のように見えます:

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + getFirstName().toLowerCase().hashCode();
    result = 31 * result + getLastName().toLowerCase().hashCode();
    return result;
}

私の質問は、オーバーライドされたhashCode()メソッドに関するものです。equals(Object)メソッドによって2つのオブジェクトが等しいと見なされた場合、2つのオブジェクトに同じ値を返すにはhashCode()が必要であることを知っています。私の腸は、このhashCode()がコントラクトに違反する場合があると言っています。

オーバーライドされたequals(Object)メソッドでequalsIgnoreCase(String)メソッドを使用し、コントラクトに違反しないハッシュコードを生成するための許容可能な方法はありますか?

4

4 に答える 4

4
@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + characterwiseCaseNormalize(getFirstName()).hashCode();
    result = 31 * result + characterwiseCaseNormalize(getLastName()).hashCode();
    return result;
}

private static String characterwiseCaseNormalize(String s) {
    StringBuilder sb = new StringBuilder(s);
    for(int i = 0; i < sb.length(); i++) {
        sb.setCharAt(i,Character.toLowerCase(Character.toUpperCase(sb.charAt(i))));
    }
    return sb.toString();
}

これは、を使用して定義されhashCodeたものと一致します。原則として、の契約によれば、これはそれが事実であることに依存しているようですequalsequalsIgnoreCaseequalsIgnoreCase

Character.toLowerCase(Character.toUpperCase(c1))==Character.toLowerCase(Character.toUpperCase(c2))

いつでも

Character.toLowerCase(c1)==Character.toLowerCase(c2).  

それが真実であるという証拠はありませんが、equalsIgnoreCaseのOpenJDK実装は、実際にはこのメソッドと一貫してそれを実行します。対応する文字が等しいかどうか、次に大文字のバージョンが等しいかどうか、次に大文字のバージョンの小文字のバージョンが等しいかどうかをチェックします。

于 2013-05-07T14:57:23.767 に答える
2

あなたが正しいです。s1,s2すべての1文字の文字列をループして、そのペアを見つけることができますs1.equalsIgnoreCase(s2) && !s1.toLowerCase().equals(s2.toLowerCase())。かなりのペアがあります。例えば

s1=0049   'LATIN CAPITAL LETTER I'
s2=0131   'LATIN SMALL LETTER DOTLESS I'

s1.lowercase = 0069   'LATIN SMALL LETTER I'
s2.lowercase = 0131   itself

ロケールによっても異なります。s1の場合、トルコ語とアゼルバイジャン語では小文字にU + 0131を使用します(http://www.fileformat.info/info/unicode/char/0049/index.htmを参照) 。

于 2013-03-26T04:09:36.890 に答える
1

あなたは心配するのは正しいです。 の契約書を読んでくださいequalsIgnoreCase

次の少なくとも1つが当てはまる場合、2つの文字c1とc2は、大文字と小文字を区別せずに同じと見なされます。

  • 2つの文字は同じです(==演算子で比較)
  • Character.toUpperCase(char)メソッドを各文字に適用すると、同じ結果が得られます。
  • Character.toLowerCase(char)メソッドを各文字に適用すると、同じ結果が得られます。

したがって、大文字に変換したときに等しいが、その逆ではない文字がある場合は問題が発生します。

大文字に変換すると2文字のシーケンスに変わるドイツ語の文字ßの例を見てみましょう。つまり、文字列「ß」と「SS」は「equalsIgnoreCase」ですが、小文字に変換すると同じ表現にはなりません。SS

したがって、ここでのアプローチは壊れています。残念ながら、ここでニーズを適切に表現するhashCodeを設計できるかどうかはわかりません。

于 2013-03-26T04:10:19.947 に答える
1

hashCode()との整合性を記述するという点では、両方でベースのケースマッピングをequals()使用するかCharacter、両方でベースのケースマッピングを使用する必要がありますString。私の他の回答では、hashCode()usingCharacterベースのケースマッピングを作成する方法を示しました。しかし、別の解決策があります。それは、equals()代わりにStringベースのケースマッピングを使用するように変更することです。(ベースのケースマッピングをString.equalsIgnoreCase()使用していることに注意してください。)Character

@Override
public boolean equals(Object o) {
    if (o == this) return true;
    if (! (o instanceof Player)) return false;
    Player p = (Player) o;
    return getFirstName().toLowerCase().equals(p.getFirstName().toLowerCase()) && 
        getLastName().toLowerCase().equals(p.getLastName().toLowerCase());
}
于 2013-05-08T14:53:09.193 に答える