web-dev-qa-db-ja.com

Java文字列でのhashCode()の一貫性

Java文字列のhashCode値は、( String.hashCode() )として計算されます。

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

次の式がfalseと評価される状況(JVMバージョン、ベンダーなど)はありますか?

boolean expression = "This is a Java string".hashCode() == 586653468

アップデート#1: 答えが「はい、そのような状況がある」と主張する場合-「これはJava文字列」.hashCode()!= 586653468である場合の具体例を挙げてください。可能な限り/コンクリート。

アップデート#2: hashCode()の実装の詳細に依存することは、一般的に悪いことは誰もが知っています。ただし、具体的にはString.hashCode()について話しているので、String.hashCode()に焦点を合わせてください。この質問の文脈では、Object.hashCode()はまったく関係ありません。

128
knorv

そのドキュメントはJava 1.2までさかのぼって見ることができます。

一般的にが同じままのハッシュコードの実装に依存するべきではないのは事実ですが、Java.lang.Stringの動作が文書化されているため、これを変更すると既存の契約に違反するものとしてカウントされます。

可能な限り、ハッシュコードがバージョン間で同じであることに依存するべきではありません-しかし、私の心ではJava.lang.Stringはアルゴリズムhasが指定されているという理由だけで特別なケースです...もちろん、アルゴリズムが指定される前のリリースとの互換性を放棄してもかまいません。

96
Jon Skeet

JDK 1.0および1.1および1.2以上について何かを見つけました。

JDK 1.0.xおよび1.1.xでは、長い文字列のhashCode関数は、n番目の文字ごとにサンプリングすることで機能しました。これにより、同じ値への多くの文字列ハッシュができ、Hashtableルックアップが遅くなります。 JDK 1.2では、これまでの結果に31を掛けて、次の文字を順番に追加するように機能が改善されました。これは少し遅くなりますが、衝突を回避するのにはるかに優れています。ソース: http://mindprod.com/jgloss/hashcode.html

ハッシュコードの代わりにCRC32またはMD5を使用するのは良いことです-議論も心配もありません...

18
ReneS

特定の値に等しいハッシュコードに依存しないでください。同じ実行内で一貫した結果を返すだけです。 APIドキュメントには次のように書かれています:

HashCodeの一般的な契約は次のとおりです。

  • Javaアプリケーションの実行中に同じオブジェクトで複数回呼び出されるときは常に、オブジェクトの等価比較で使用される情報が変更されない限り、hashCodeメソッドは常に同じ整数を返す必要があります。この整数は、アプリケーションのある実行から同じアプリケーションの別の実行まで一貫している必要はありません。

EDIT String.hashCode()のjavadocはStringのハッシュコードの計算方法を指定するため、これに違反するとパブリックAPI仕様に違反します。

8
Martin OConnor

上で述べたように、一般に、クラスのハッシュコードが同じままであることに頼るべきではありません。 同じVMでの同じアプリケーションの後続の実行でも、異なるハッシュ値を生成します。知る限り、Sun JVMのハッシュ関数は実行ごとに同じハッシュを計算しますが、それは保証されていません。

これは理論的ではないことに注意してください。 Java.lang.Stringのハッシュ関数 変更されました JDK1.2(古いハッシュは、URLやファイル名などの階層文字列に問題がありました。最後に)。

Java.lang.Stringは特別なケースです。hashCode()のアルゴリズムが(現在)文書化されているため、おそらくこれに依存できます。私はまだ悪い習慣だと思っています。文書化された特別なプロパティを持つハッシュアルゴリズムが必要な場合は、1つだけ記述してください:-)。

4
sleske

心配するべきもう1つの(!)問題は、Javaの初期/後期バージョン間の実装の変更の可能性です。実装の詳細が明確に設定されているとは思わないため、future Javaバージョンへのアップグレードが問題を引き起こす可能性があります。

一番下の行は、hashCode()の実装に依存しません。

おそらく、このメカニズムを使用して、実際に解決しようとしている問題を強調することができ、それはより適切なアプローチを強調します。

3
Brian Agnew

あなたの質問に答えるためだけで、議論を続けないためです。 Apache Harmony JDK実装は、異なるアルゴリズムを使用しているようです。少なくとも、まったく異なるように見えます。

Sun JDK

public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

Apache Harmony

public int hashCode() {
    if (hashCode == 0) {
        int hash = 0, multiplier = 1;
        for (int i = offset + count - 1; i >= offset; i--) {
            hash += value[i] * multiplier;
            int shifted = multiplier << 5;
            multiplier = shifted - multiplier;
        }
        hashCode = hash;
    }
    return hashCode;
}

自分でチェックしてください...

2
ReneS

変更や互換性のないVMが心配な場合は、既存のハッシュコード実装を独自のユーティリティクラスにコピーし、それを使用してハッシュコードを生成します。

2
Sam Barnum

ハッシュコードは、文字列内の文字のASCII値に基づいて計算されます。

これは、Stringクラスの実装は次のとおりです。

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        hash = h = isLatin1() ? StringLatin1.hashCode(value)
                              : StringUTF16.hashCode(value);
    }
    return h;
}

ハッシュコードの衝突は避けられません。たとえば、文字列「Ea」と「FB」は2236と同じハッシュコードを提供します

0
Lourdes