Android性能優(yōu)化-SparseArray

SparseArray是Android框架獨(dú)有的類胯甩。是Google官方推薦當(dāng)key為整形的時(shí)候,(key堪嫂,value)的形式偎箫,替代HashMap的一種存儲(chǔ)結(jié)構(gòu),使用SparseArray可以避免java的自動(dòng)裝箱皆串,拆箱的過程淹办,而且相比較HashMap,Sparse更節(jié)省內(nèi)存恶复。

我們?cè)谑褂靡环N數(shù)據(jù)結(jié)構(gòu)的時(shí)候會(huì)關(guān)注怜森,這種結(jié)構(gòu)的效率速挑,包括時(shí)間和空間兩點(diǎn)。實(shí)際測(cè)試了一下副硅。

正向插入時(shí)

long btimeSp=System.currentTimeMillis();
SparseArray sparse=new SparseArray();
for(int i=0;i<100000;i++){
sparse.put(i,i);
}
long ftimeSp=System.currentTimeMillis()-btimeSp;

long btimeHash=System.currentTimeMillis();
HashMap hash=new HashMap();
for(int i=0;i<100000;i++){
hash.put(i,i);
}
long ftimeHa=System.currentTimeMillis()-btimeHash;
正向插入100 000條 Sparse: 17ms Hash: 44ms

正向插入的時(shí)候姥宝,SparseArray相比較HashMap要快一倍以上。

反向插入時(shí)

long btimeSp=System.currentTimeMillis();
SparseArray sparse=new SparseArray();
for(int i=100000;i>0;i--){
sparse.put(i,i);
}
long ftimeSp=System.currentTimeMillis()-btimeSp;

long btimeHa=System.currentTimeMillis();
HashMap hash=new HashMap();
for(int i=100000;i>0;i--){
hash.put(i,i);
}
long ftimeHa=System.currentTimeMillis()-btimeHa;
反向插入100 000條 Sparse: 6561ms Hash: 42ms

可見反向插入的時(shí)候在數(shù)據(jù)量到達(dá)10 0000級(jí)別的時(shí)候恐疲,SparseArray比正向插入要慢百倍以上(SparseArray在android性能優(yōu)化典范中說到適合數(shù)量級(jí)在千以內(nèi))

但如果反向插入1 0000的時(shí)候占用內(nèi)存 SparseArray和HashMap 10 0000條的時(shí)候Sparse: 82ms Hash: 6ms腊满,這時(shí)就到了相差了10倍左,1000的時(shí)候就比較相近了(注意這里我們看的是SparseArray在最差的情況下)

這樣可以看出SparseArray的插入效率并不是很穩(wěn)定,至于其中的原因可以在源碼中找到培己。

public void put(int key, E value) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
...
}

static int binarySearch(int[] array, int size, int value) {
int lo = 0;
int hi = size - 1;

while (lo <= hi) {
    final int mid = (lo + hi) >>> 1;
    final int midVal = array[mid];

    if (midVal < value) {
        lo = mid + 1;
    } else if (midVal > value) {
        hi = mid - 1;
    } else {
        return mid;  // value found
    }
}
return ~lo;  // value not present

}
put添加數(shù)據(jù)的時(shí)候碳蛋,會(huì)使用二分查找法和之前的key比較當(dāng)前我們添加的元素的key的大小,然后按照從小到大的順序排列好漱凝,SparseArray通過Key值的大小維持了一個(gè)有序的數(shù)組疮蹦。

Sparse: 3.52M Hash: 8.14M

我們已經(jīng)看到了相比較HashMap SparseMap的優(yōu)勢(shì)是更節(jié)省內(nèi)存诸迟,時(shí)間效率上SparseMap沒有HashMap穩(wěn)定茸炒,但相對(duì)Android對(duì)于內(nèi)存更加敏感,使用SparseArray顯然更加合適阵苇,SparseArray是時(shí)間和空間的一個(gè)折中壁公,而且我們測(cè)試的是在10 0000這個(gè)數(shù)量級(jí)(而且是最差的情況),在數(shù)量級(jí)較大的時(shí)候绅项,不停地插入數(shù)據(jù)紊册,由于存儲(chǔ)數(shù)據(jù)的是數(shù)組,這樣會(huì)創(chuàng)建新的數(shù)組快耿,數(shù)據(jù)量較大的時(shí)候每次創(chuàng)建與復(fù)制的過程都很耗時(shí)囊陡,SparseArray會(huì)在index超出的時(shí)候重新創(chuàng)建一個(gè)長度是原來二倍的數(shù)組(SparseArray 的key和value是用兩個(gè)數(shù)組存儲(chǔ))

mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
mValues = GrowingArrayUtils.insert(mValues, mSize, i, value)
GrowingArrayUtils的insert

public static int[] insert(int[] array, int currentSize, int index, int element) {
assert currentSize <= array.length;
if (currentSize + 1 <= array.length) {
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}
增長長度

public static int growSize(int currentSize) {
return currentSize <= 4 ? 8 : currentSize * 2;
}
這里我們看到了插入數(shù)據(jù)時(shí)的移位的過程,而且超出數(shù)組界限會(huì)創(chuàng)建一個(gè)長度為原來2倍的數(shù)組掀亥,數(shù)據(jù)量越大put時(shí)需要移位的數(shù)據(jù)就越多撞反,這也是我們不希望看到的,這也就是推薦使用數(shù)量級(jí)在千以內(nèi)的原因搪花。但如果在千數(shù)量級(jí)內(nèi)遏片,SparseArray的性能顯然要好很多,查找的過程是二分法查找撮竿,效率也相當(dāng)?shù)牟诲e(cuò)吮便,內(nèi)存比HashMap至少節(jié)省一半。SparseArray還避免了java的自動(dòng)裝箱的行為幢踏,key和value分別使用兩個(gè)數(shù)組存儲(chǔ)key直接存儲(chǔ)的是int髓需,對(duì)于HashMap,我們?cè)趐ut的時(shí)候就執(zhí)行了裝箱房蝉。

我們傳入的是int授账,但這里會(huì)裝箱為Integer枯跑,當(dāng)我們?nèi)≈档臅r(shí)候執(zhí)行的是 i.intValue(),對(duì)于HashMap又會(huì)將<Key白热,Value>封裝成為HashMapEntry敛助,int占用 4個(gè)字節(jié),對(duì)于Integer占用的字節(jié)數(shù)屋确,至少包含實(shí)例數(shù)據(jù)和指向類數(shù)據(jù)的指針纳击,實(shí)例數(shù)據(jù)是int占用4字節(jié),指向類數(shù)據(jù)的指針占用4字節(jié)攻臀,Integer至少包含8字節(jié)焕数。

總結(jié):

1.SparseArray只能在key為int時(shí)替換HashMap。

2.SparseArray不適用于數(shù)量級(jí)過大的存儲(chǔ)刨啸,通常在千級(jí)以內(nèi)堡赔。

3.SparseArray效率比較HashMap不是很穩(wěn)定,數(shù)量級(jí)較小效率較高设联,但比較HashMap更節(jié)省內(nèi)存善已。

還有兩個(gè)類SparseBooleanArray和SparseIntArray分別對(duì)應(yīng)的是特定 int -> boolean ,int -> int

對(duì)于key值比較大的可以使用LongSparseArray

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末离例,一起剝皮案震驚了整個(gè)濱河市换团,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宫蛆,老刑警劉巖艘包,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異耀盗,居然都是意外死亡想虎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門叛拷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舌厨,“玉大人,你說我怎么就攤上這事胡诗〉讼撸” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵煌恢,是天一觀的道長骇陈。 經(jīng)常有香客問我,道長瑰抵,這世上最難降的妖魔是什么你雌? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上婿崭,老公的妹妹穿的比我還像新娘拨拓。我一直安慰自己,他們只是感情好氓栈,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布渣磷。 她就那樣靜靜地躺著,像睡著了一般授瘦。 火紅的嫁衣襯著肌膚如雪醋界。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天提完,我揣著相機(jī)與錄音形纺,去河邊找鬼。 笑死徒欣,一個(gè)胖子當(dāng)著我的面吹牛逐样,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播打肝,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼脂新,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了闯睹?” 一聲冷哼從身側(cè)響起戏羽,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤担神,失蹤者是張志新(化名)和其女友劉穎楼吃,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妄讯,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孩锡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亥贸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躬窜。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炕置,靈堂內(nèi)的尸體忽然破棺而出荣挨,到底是詐尸還是另有隱情,我是刑警寧澤朴摊,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布默垄,位于F島的核電站,受9級(jí)特大地震影響甚纲,放射性物質(zhì)發(fā)生泄漏口锭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一介杆、第九天 我趴在偏房一處隱蔽的房頂上張望鹃操。 院中可真熱鬧韭寸,春花似錦、人聲如沸荆隘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椰拒。三九已至莫其,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耸三,已是汗流浹背乱陡。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仪壮,地道東北人憨颠。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像积锅,于是被迫代替她去往敵國和親爽彤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359