標籤:android blog http io ar sp for java on
本篇文章轉載他人,原文地址:http://www.xyczero.com/blog/article/16/
起因
今天在項目中遇到一個很”奇葩”的問題。情況大致是這樣的: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/4threshold = (newCapacity >> 1) + (newCapacity >> 2); <!-- Java -->//閾值取容量*負載因子或最大容量+1間的小值threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
小結
所以總結來看HashMap在不同平台或不同語言中的實現細節是不一樣的,吃一塹,長一智,反正以後切記,牽扯到順序時HashMap真的不適合!
HashMap在Android和Java中的不同實現