閱讀356 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Java中String的hash函數分析

JDK6的源碼:

    /**
     * Returns a hash code for this string. The hash code for a
     * <code>String</code> object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using <code>int</code> arithmetic, where <code>s[i]</code> is the
     * <i>i</i>th character of the string, <code>n</code> is the length of
     * the string, and <code>^</code> indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    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;
    }


以字符串"123"為例:

字符'1'的ascii碼是49

hashCode = (49*31 + 50)*31 + 51

或者這樣看:

hashCode=('1' * 31 + '2' ) * 31 + '3'

可見實際可以看作是一種權重的算法,在前麵的字符的權重大。

這樣有個明顯的好處,就是前綴相同的字符串的hash值都落在鄰近的區間。

好處有兩點:

1.可以節省內存,因為hash值在相鄰,這樣hash的數組可以比較小。比如當用HashMap,以String為key時。

2.hash值相鄰,如果存放在容器,比好HashSet,HashMap中時,實際存放的內存的位置也相鄰,則存取的效率也高。(程序局部性原理)


以31為倍數,原因了31的二進製全是1,則可以有效地離散數據。

最後看下,兩個字符串,由Eclipse生成的代碼是如何計算hash值的:

public class Name{
	String firstName;
	String lastName;
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((firstName == null) ? 0 : firstName.hashCode());
		result = prime * result
				+ ((lastName == null) ? 0 : lastName.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Name other = (Name) obj;
		if (firstName == null) {
			if (other.firstName != null)
				return false;
		} else if (!firstName.equals(other.firstName))
			return false;
		if (lastName == null) {
			if (other.lastName != null)
				return false;
		} else if (!lastName.equals(other.lastName))
			return false;
		return true;
	}	
}

可見,還是以31為倍數, hashCode = firstName.hashCode() * 31 + lastName.hashCode() 。

BTW:Java的字符串的hash做了緩存,第一次才會真正算,以後都是取緩存值。

eclipse生成的equals函數質量也很高,各種情況都考慮到了。


總結:字符串hash函數,不僅要減少衝突,而且要注意相同前綴的字符串生成的hash值要相鄰。







最後更新:2017-04-02 06:52:21

  上一篇:go JAVASCRIPT學習筆記基礎(二)
  下一篇:go Struts中s:url的應用