通常我們?cè)谑褂胟ey-value存儲(chǔ)數(shù)據(jù)時(shí)买决,隨手就會(huì)打出HashMap的代碼,當(dāng)數(shù)據(jù)量較小時(shí)带猴,還可以昔汉,當(dāng)數(shù)量比較多的時(shí)候,如果是PC機(jī)上拴清,也還說(shuō)得過(guò)去靶病,但是如果使用設(shè)備是手機(jī)等移動(dòng)設(shè)備,這是就要慎重了口予。因?yàn)槭謾C(jī)的內(nèi)存非常寶貴娄周,不像PC那樣不計(jì)后果的使用,內(nèi)存使用不當(dāng)很容易就會(huì)引起OOM的問(wèn)題沪停。那Android開發(fā)團(tuán)隊(duì)煤辨,也為我們找到了HashMap的替代品ArrayMap。
官方對(duì)ArrayMap也有說(shuō)明:它不是一個(gè)適應(yīng)大數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)木张,相比傳統(tǒng)的HashMap速度要慢众辨,因?yàn)椴檎曳椒ㄊ嵌址ǎ⑶耶?dāng)你刪除或者添加數(shù)據(jù)時(shí)窟哺,會(huì)對(duì)空間重新調(diào)整泻轰,在使用大量數(shù)據(jù)時(shí),效率并不明顯且轨,低于50%浮声。
所以ArrayMap是犧牲了時(shí)間換區(qū)空間。在寫手機(jī)app時(shí)旋奢,適時(shí)的使用ArrayMap泳挥,會(huì)給內(nèi)存使用帶來(lái)可觀的提升。
那HashMap和ArrayMap到底不同在哪呢至朗,個(gè)人總結(jié)有以下方面:
1屉符、存儲(chǔ)方式不同。
HashMap內(nèi)部有一個(gè)HashMapEntry[]對(duì)象,每一個(gè)鍵值對(duì)都存儲(chǔ)在這個(gè)對(duì)象里矗钟,當(dāng)使用put方法添加鍵值對(duì)時(shí)唆香,就會(huì)new一個(gè)HashMapEntry對(duì)象
[java]view plaincopy
@OverridepublicV?put(K?key,?V?value)?{
if(key?==null)?{
returnputValueForNullKey(value);
}
inthash?=?secondaryHash(key);
HashMapEntry[]?tab?=?table;
intindex?=?hash?&?(tab.length?-1);
//先查找有沒(méi)有對(duì)應(yīng)的key值,如果有吨艇,就改寫value躬它,并返回改寫前的value值:oldValue
for(HashMapEntry?e?=?tab[index];?e?!=null;?e?=?e.next)?{
if(e.hash?==?hash?&&?key.equals(e.key))?{
preModify(e);
V?oldValue?=?e.value;
e.value?=?value;
returnoldValue;
}
}
//?No?entry?for?(non-null)?key?is?present;?create?one
modCount++;
if(size++?>?threshold)?{
//擴(kuò)容,雙倍
tab?=?doubleCapacity();
index?=?hash?&?(tab.length?-1);
}
addNewEntry(key,?value,?hash,?index);
returnnull;
}
//創(chuàng)建對(duì)象存儲(chǔ)鍵值對(duì)
voidaddNewEntry(K?key,?V?value,inthash,intindex)?{
table[index]?=newHashMapEntry(key,?value,?hash,?table[index]);
}
ArrayMap的存儲(chǔ)中沒(méi)有Entry這個(gè)東西东涡,他是由兩個(gè)數(shù)組來(lái)維護(hù)的
[java]view plaincopy
int[]?mHashes;
Object[]?mArray;
mHashes數(shù)組中保存的是每一項(xiàng)的HashCode值冯吓,mArray中就是鍵值對(duì),每?jī)蓚€(gè)元素代表一個(gè)鍵值對(duì)疮跑,前面保存key组贺,后面的保存value,我們看看下面代碼的結(jié)果
[java]view plaincopy
arraymap?=newHashMap();
a.put("a","a_value");
a.put("b","b_value");
執(zhí)行上面代碼后祖娘,arraymap中的存儲(chǔ)是這樣的
是不是能清楚地看到ArrayMap的存儲(chǔ)了失尖,這種存儲(chǔ)在put代碼中如下
[java]view plaincopy
mHashes[index]?=?hash;
mArray[index<<1]?=?key;
mArray[(index<<1)+1]?=?value;
2、添加數(shù)據(jù)時(shí)擴(kuò)容時(shí)的處理不一樣
先來(lái)看看HashMap
[java]view plaincopy
if(size++?>?threshold)?{
tab?=?doubleCapacity();
index?=?hash?&?(tab.length?-1);
}
doubleCapacity進(jìn)行雙倍擴(kuò)容贿条,它的代碼中有這么一句話
[java]view plaincopy
HashMapEntry[]?newTable?=?makeTable(newCapacity);
最終雹仿,這個(gè)newTable將作為擴(kuò)容后的新對(duì)象返回,那么makeTable做了什么呢整以,如下:
[java]view plaincopy
privateHashMapEntry[]?makeTable(intnewCapacity)?{
@SuppressWarnings("unchecked")?HashMapEntry[]?newTable
=?(HashMapEntry[])newHashMapEntry[newCapacity];
table?=?newTable;
threshold?=?(newCapacity?>>1)?+?(newCapacity?>>2);//?3/4?capacity
returnnewTable;
}
我們清楚地看到胧辽,這里進(jìn)行了new操作,重新創(chuàng)建對(duì)象公黑,開銷很大邑商。
那么ArrayMap呢,看看吧
[java]view plaincopy
//如果容量不夠
ize?>=?mHashes.length)?{
finalintn?=?mSize?>=?(BASE_SIZE*2)???(mSize+(mSize>>1))
:?(mSize?>=?BASE_SIZE???(BASE_SIZE*2)?:?BASE_SIZE);
if(DEBUG)?Log.d(TAG,"put:?grow?from?"+?mHashes.length?+"?to?"+?n);
finalint[]?ohashes?=?mHashes;
finalObject[]?oarray?=?mArray;
//分配數(shù)組
allocArrays(n);
if(mHashes.length?>0)?{
if(DEBUG)?Log.d(TAG,"put:?copy?0-"+?mSize?+"?to?0");
//特別注意這凡蚜,是copy人断,而不是new,效率提升
System.arraycopy(ohashes,0,?mHashes,0,?ohashes.length);
System.arraycopy(oarray,0,?mArray,0,?oarray.length);
}
//釋放無(wú)用空間朝蜘,收縮數(shù)組
freeArrays(ohashes,?oarray,?mSize);
}
ArrayMap用的是copy數(shù)據(jù)恶迈,所以效率相對(duì)要高。
3谱醇、ArrayMap提供了數(shù)組收縮的功能暇仲,在clear或remove后,會(huì)重新收縮數(shù)組副渴,是否空間
4奈附、ArrayMap采用二分法查找(見 android.support.v4.util.ContainerHelpers中的binarySearch方法)