(1) JavaのhashCode()で31を掛け算する理由について
(1-1) ハッシュの計算のソースコード
(1-2) ソースコードに登場する代表的な変数
(1-3) ソースコード解説
(1) JavaのhashCode()で31を掛け算する理由について
Javaでハッシュを計算するhashCode()について、実際のプログラムを見てみると「31」で掛け算する計算を行っていますが、今日はこの「31の意味」について書きたいと思います。
(1-1) ハッシュの計算のソースコード
以下がStringクラスのhashCode()メソッドのソースコードです。Eclipseなどで「[String変数].hashCode()」の部分を右クリックして「Open Declaration」を押下すると、hashCodeメソッドのソースコードを見る事ができます。
(ソースコード)
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
(図111)Open Declaration
(1-2) ソースコードに登場する代表的な変数
ソースコードを理解するために、まず代表的な変数の意味を抑えます。
(表)
項目 | 説明 |
hash | ハッシュ値を保持するための変数です。これはStringクラスのメンバ変数として下記のように定義されているものです。
private int hash; // Default to 0 初期値は「0」ですが、一回計算するとその結果が格納されるため、次回以降に同じ文字列に対しては値が入った状態になります。 |
value | 文字列の各文字を1文字ずつ保持するためのchar型の配列です。これはStringクラスのメンバ変数として下記のように定義されているもので、文字列の生成時に値が入ります。
private final char value[] |
(1-3) ソースコード解説
先ほどのソースコードにコメントを追記したバージョンを掲載します。ただし「h = 31 * h + val[i];」の部分は難しいので、コードの後の(補足)欄で更に補足します。
(ソースコード)
public int hashCode() { //前回の計算結果(hash)をhに格納 int h = hash; //前回の計算結果が0(なし) AND 文字列長>0(文字列あり)の場合 if (h == 0 && value.length > 0) { //文字列をchar型の配列に格納(1文字ずつ計算に当てはめるため) char val[] = value; //文字列長の長さ(文字数)だけループ for (int i = 0; i < value.length; i++) { // 31 * h => 1つ前のループまでの結果×31倍 // + val[i] => 次の文字の10進数のASCIIコードを加算 // (char型は足し算する時は整数とみなされる性質を利用) h = 31 * h + val[i]; } hash = h; } return h; }
h[n] = 31 * h[n-1] + val[n-1] ・・・(★) = 31 * (31 * h[n-2] + val[n-2]) + val[n-1] = 31 * (31 * (31 * h[n-3] + val[n-3]) + val[n-2]) + val[n-1] = 31 * (31 * (31 * (31 * h[n-4] + val[n-4]) + val[n-3]) + val[n-2]) + val[n-1] ・・・(続く) = val[0]*31^(n-1) + val [1]*31^(n-2) + ... + val [n-1] ・・・(▲)
(5ビットシフトして元の数を引く)
31 * i == (i << 5) - i