通常我們?cè)谑褂胟ey-value存儲(chǔ)數(shù)據(jù)時(shí),隨手就會(huì)打出HashMap的代碼,當(dāng)數(shù)據(jù)量較小時(shí),還可以,當(dāng)數(shù)量比較多的時(shí)候,如果是PC機(jī)上,也還說得過去,但是如果使用設(shè)備是手機(jī)等移動(dòng)設(shè)備,這是就要慎重了。因?yàn)槭謾C(jī)的內(nèi)存非常寶貴,不像PC那樣不計(jì)后果的使用,內(nèi)存使用不當(dāng)很容易就會(huì)引起OOM的問題。那Android開發(fā)團(tuán)隊(duì),也為我們找到了HashMap的替代品ArrayMap。
官方對(duì)ArrayMap也有說明:它不是一個(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)存使用帶來可觀的提升。
那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);
//先查找有沒有對(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ǔ)中沒有Entry這個(gè)東西,他是由兩個(gè)數(shù)組來維護(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í)的處理不一樣
先來看看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);
}
//釋放無用空間,收縮數(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方法)