歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

HashMap在Android和Java中的不同實現

起因

今天在項目中遇到一個很"奇葩"的問題。情況大致是這樣的:Android終端和服務器(Spring),完全相同的字符串鍵值對放入HashMap中竟然順序不一樣,這直接導致了服務器和Android終端用HmacSHA256算法加密出的摘要也不一樣,服務器也就無法進行正確的數據驗證。

然後帶著郁悶的心情給程序加斷點進行原因尋找,發現原來是HashMap的中服務器和終端雙方對於同樣的key存放順序竟然不一樣!

在HashCode產生沖突的情況下,不同的key在HashMap中存入的位置應該是相同的,即使在hashCode產生沖入,如果key-value put的順序相同,其存放的位置也應該是相同的。

尋找,解決

所以問題就應該出在HashMap上,只能去查看Java和Android關於HashMap的源碼了,發現兩者的hashCode()方法竟然不一樣,小小激動了一下,可仔細一看,發現Android只是優化Java中的hashCode()方法,使其更加易於閱讀而已,但所運用的原理還是一樣的,真是=。=。
具體代碼比較如下:

<!-- Android -->
@Override public int hashCode() {
    int hash = hashCode;
    if (hash == 0) {
        if (count == 0) {
            return 0;
        }
        final int end = count + offset;
        final char[] chars = value;
        for (int i = offset; i < end; ++i) {
            hash = 31*hash + chars[i];
        }
        hashCode = hash;
    }
    return hash;
}

<!-- Java-->
public int hashCode() {
	int h = hash;
    int len = count;
	if (h == 0 && len > 0) {
	    int off = offset;
	    char val[] = value;
        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

無奈,我只能繼續在源碼裡查看比較,最後發現原來是兩者的默認構造函數不一樣,本質上就是兩者的table大小不一樣,Java中的table默認大小是16×0.75=12(容量×負載因子),而Android中table的默認大小是2,所以即使是同樣的字符串按同樣的順序放入HashMap中它們的key值存放順序也會不一樣。

<!-- Android -->
private static final Entry[] EMPTY_TABLE
        = new HashMapEntry[MINIMUM_CAPACITY >>> 1];
//默認構造函數
public HashMap() {
    table = (HashMapEntry<K, V>[]) EMPTY_TABLE;
    threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
}

<!-- Java -->
static final int DEFAULT_INITIAL_CAPACITY = 16;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//默認構造函數
public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY,DEFAULT_LOAD_FACTOR);
}

其實仔細讀源碼會發現,在Android中所實現的HashMap類關於"阈值(threshold )"的設定也已經和Java不同了,具體請看截取的源碼:

<!-- Android -->
//阈值固定取其table大小的3/4
threshold = (newCapacity >> 1) + (newCapacity >> 2); 

<!-- Java -->
//阈值取容量*負載因子或最大容量+1間的小值
threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);

小結

所以總結來看HashMap在不同平台或不同語言中的實現細節是不一樣的,吃一塹,長一智,反正以後切記,牽扯到順序時HashMap真的不適合!

更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11

Copyright © Linux教程網 All Rights Reserved