123

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()は、この質問とは無関係です。


  • 実際にこの機能が必要ですか。なぜあなたは正確な価値が必要なのですか? - Brian Agnew
  • @Brian:String.hashCode()の規約を理解しようとしています。 - knorv
  • @Knorvそれがどのように機能するかを正確に理解する必要はありません - 契約とその最終的な意味を理解することがより重要です。 - mP.
  • @ mP:ご意見をお寄せいただきありがとうございますが、決定するのは私次第だと思います。 - knorv
  • なぜ彼らは最初のキャラクターに最大の力を与えたのですか?余分な計算を保存するために速度を最適化したいときは、前のものの力を保存しますが、前のものは最後の文字から最初の文字までのものです。これはキャッシュミスもあることを意味します。 s [0] + s [1] * 31 + s [2] * 31 ^ 2 + ... + s [n-1] * 31 ^ [nのアルゴリズムを使用する方が効率的ではありません。 -1] - android developer

7 답변


91

その文書はJava 1.2までさかのぼります。

それは本当ですが一般にハッシュコードの実装が同じであることに頼るべきではありません。java.lang.Stringしたがって、それを変更すると既存の契約が破られることになります。

可能であれば、バージョン間でハッシュコードが同じであることに頼るべきではありません。java.lang.Stringアルゴリズムが単純だからです。持っているアルゴリズムが指定される前にリリースとの互換性を放棄しても構わない限り、...は指定されています。


  • Stringのドキュメント化された動作は、Java 1.2以降で規定されています。APIのv1.1では、ハッシュコードの計算はStringクラスに対して指定されていません。 - Martin OConnor
  • この場合は、独自のハッシュコードを作成したほうがいいでしょうか。 - Felype
  • @Felype:君がここで何を言おうとしているのか本当にわからない、怖い。 - Jon Skeet
  • @JonSkeetつまり、この場合、移植性を与えるために、独自のハッシュを生成する独自のコードを作成する可能性があります。それは...ですか? - Felype
  • @Felype:どのような種類の移植性について話しているのか、実際には「この場合」とはどういう意味なのか、まったく明確ではありません。 - 具体的なシナリオは?私はあなたが新しい質問をするべきだと思います。 - Jon Skeet

18

私は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を使用しても問題ありません。ディスカッションも心配もまったく必要ありません。


10

ハッシュコードが特定の値に等しいとは限りません。それが同じ実行内で一貫した結果を返すということだけです。 APIドキュメントは次のように言っています。

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

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

編集String.hashCode()のjavadocは、Stringのハッシュコードの計算方法を指定しているので、これに違反すると公開APIの仕様に違反します。


  • あなたの答えは有効ですが、尋ねられた特定の質問には対応していません。 - knorv
  • それは一般的なハッシュコード契約 - しかし特定Stringに対するコントラクトは、アルゴリズムの詳細を示し、この一般コントラクトIMOを事実上オーバーライドします。 - Jon Skeet

4

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

これは理論的なことではありません。 java.lang.Stringのハッシュ関数かわったJDK 1.2では(URLやファイル名のような階層的な文字列で問題がありました。文字列に対して同じハッシュが生成される傾向があったため、末尾が異なるだけでした)。

hashCode()のアルゴリズムは(現在)文書化されているので、java.lang.Stringは特別な場合です。したがって、おそらくそれに頼ることができます。私はまだそれを悪い習慣だと思います。特別な、文書化された特性を持つハッシュアルゴリズムが必要な場合は、ただ1つ書いてください:-)


  • しかし、アルゴリズムはJDK 1.2より前のドキュメントで指定されていましたか?そうでない場合は、別の状況です。このアルゴリズムは現在ドキュメントで規定されているので、それを変更することは公共の契約に対する重大な変更となるでしょう。 - Jon Skeet
  • (私はそれを1.1と覚えています。)オリジナルの(より貧弱な)アルゴリズムが文書化されました。間違っています。記載されているアルゴリズムは、実際にはArrayIndexOutOfBoundsExceptionをスローしました。 - Tom Hawtin - tackline
  • @ John Skeet:ああ、String.hashCode()のアルゴリズムが文書化されていることを知りませんでした。もちろんそれは物事を変えます。私のコメントを更新しました。 - sleske

3

心配しなければならないもう1つの(!)問題は、Javaの初期/後期バージョン間で実装が変更される可能性があることです。実装の詳細がすぐに決まっているとは思わないので、潜在的には未来Javaバージョンは問題を引き起こす可能性があります。

肝心なのは、私はの実装に依存しないでしょうhashCode()

たぶん、あなたはこのメカニズムを使って実際にどんな問題を解決しようとしているのかを強調することができ、そしてそれはより適切なアプローチを強調するでしょう。


  • ご回答有難うございます。 「これはJava文字列です。」.hashCode()!= 586653468の場合の具体例を教えてください。 - knorv
  • いいえ、申し訳ありません。私のポイントは、あなたがテストするすべてがあなたが望む方法でうまくいくかもしれないということです。しかし、それでも保証はありません。もしあなたが'あなたがVMなどの管理権を持っている(例えば)短期プロジェクトに取り組んでいるなら、上記はあなたのために働くかもしれません。しかし、より広い世界でそれに頼ることはできません。 - Brian Agnew
  • 「将来のJavaバージョンにアップグレードすると問題が発生する可能性があります」。将来のJavaバージョンにアップグレードすると、hashCodeメソッドは完全に削除される可能性があります。または、文字列に対して常に0を返すようにします。 yaとの互換性のない変更です。問題は、Sun ^ HOracle ^ HThe JCPがそれを重大な変化と見なし、回避する価値があるかどうかです。このアルゴリズムは契約されているので、そうなることを願います。 - Steve Jessop
  • それ以来、@ SteveJessopswitch文字列に対するステートメントは、特定の固定ハッシュコードに依存するコードにコンパイルされます。Stringのハッシュコードアルゴリズムは間違いなく既存のコードを破壊するでしょう… - Holger

3

あなたの質問に答えるためだけに話し合いを続けないために。 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;
}

アパッチハーモニー

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;
}

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


  • 彼らはただクールで最適化しているだけだと思います。 :)"(乗数<< 5> - 乗数")結局のところ、ちょうど31 *乗数です... - unwind
  • チェック:同じ結果... - Carlos Heuberger
  • わかりました、それをチェックするのが面倒でした。ありがとうございます。 - ReneS
  • しかし、私の側からはっきりさせるために...ハッシュコードは内部的なものなので、ハッシュコードに頼らないでください。 - ReneS
  • @androiddeveloperそれでは、興味深い質問です - あなたのユーザー名に基づいて、私はそれを推測したはずですが。からAndroidのドキュメント契約は同じであるように見えます。s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]間違えない限り、これはAndroidがSunのStringオブジェクトの実装を変更せずに使用しているためです。 - K_7

2

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


  • 私はこれを言うつもりでした。他の答えでも問題は解決しますが、おそらく別のhashCode関数を作成することが問題を解決するための適切な解決策です。 - Nick

リンクされた質問


関連する質問

最近の質問