SparseArray分析

SparseArray分析

SparseArray是一個(gè)稀疏數(shù)組醋虏,所謂稀疏數(shù)組就是指數(shù)組中的大部分內(nèi)容值未被使用(或者為零),只有很少部分的空間被使用。因此造成了內(nèi)存空間的浪費(fèi),為了節(jié)省內(nèi)存空間扩淀,并且不影響數(shù)組中的原始內(nèi)容值,我們可以采用一種壓縮的方式來表示數(shù)組的內(nèi)容啤挎。故稀疏數(shù)組我們可以看做是普通數(shù)組的壓縮驻谆。

例如:

我們有一個(gè)5*9的數(shù)組,普通數(shù)組的表示如下

0 0 0 0 0 0
0 1 0 0 0 0 
0 0 0 0 0 0
0 4 0 0 0 0 
0 0 0 0 0 0
0 0 6 0 0 0 
0 0 0 0 0 0
0 0 0 0 0 0     
0 0 0 0 0 0
從上面我們庆聘,5*9的數(shù)組中胜臊,我們內(nèi)容只有1,4,6,其他的都沒有使用到伙判,浪費(fèi)了大部分的內(nèi)存空間象对,我們?nèi)绻褂孟∈钄?shù)組表示
5 9 3  //5表示數(shù)組有5行,9表示數(shù)組有9列宴抚,3表示數(shù)組中有三個(gè)非0數(shù)據(jù)
1 1 1  //1表示第一行 1 表示第一列 1表示元素1
3 1 4  //3表示第三行 1 表示第一列 4表示元素4
5 2 6
SparseArray的特點(diǎn):
  • SparseArray是一個(gè)稀疏的數(shù)組
  • SparseArray的key只能是int類型,避免了key的裝箱拆箱的操作勒魔,提升了性能
  • SparseArray的key的查找使用了二分的查找方式甫煞,故key是一個(gè)有序的數(shù)組
  • SparseArray的刪除是一個(gè)延遲的刪除,通過將key的value置為DELECTED冠绢,方便后面對(duì)該下標(biāo)的存儲(chǔ)的復(fù)用
  • SparseArray遇到頻繁刪除抚吠,不會(huì)觸發(fā)gc,導(dǎo)致mSize遠(yuǎn)大于有效數(shù)組的長度弟胀,造成性能損耗
  • SparseArray插入的時(shí)候楷力,在不能復(fù)用的情況下,需要將該位置及以后的數(shù)據(jù)先進(jìn)行copy移位孵户,然后再插入
使用場(chǎng)景:
  • key為整型
  • 不需要進(jìn)行頻繁的刪除
  • 元素個(gè)數(shù)相對(duì)較少
源碼分析:
private int[] mKeys;
private Object[] mValues;
private int mSize;
private boolean mGarbage = false;

public SparseArray(){
    this(10);
}

public SparseArray(int initialCapacity){
    if(initialCapacity ==0){
        mKeys = EmptyArray.INT;
        mValues = EmptyArray.OBJECT;
    }else{
        mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity);
        mKeys = new int[mValues.length];
    }
    mSize = 0;
}

構(gòu)造函數(shù)萧朝,默認(rèn)的數(shù)組的長度為10,mSize數(shù)組中的元素為0夏哭。

mKeys:0 0 0 0 0 0 0 0 0 0
mValues:null null null null null null null null null null

現(xiàn)向當(dāng)前數(shù)組中插入一個(gè)Person

public void put(int key,E value){
    //第一步通過key獲取當(dāng)前key所在的位置剪勿,使用的是二分查找
    int pos = ContainerHelper.binarySearch(mKeys,mSize,key);
    if(pos >=0){//找到了對(duì)應(yīng)的位置,直接覆蓋
        mValues[pos] = value;
    }else{//不存在
        pos = ~pos; //即將要插入的位置
        if(pos < mSize && mValues[pos] == DELETED){//可以復(fù)用該位置
            mKeys[pos] = pos;
            mValues[pos] = value;
            mSize++;
            return;
        }
        
        if(mGarbage && mSize>=mKeys.length()){//需要回收,且mSize超出了mkeys的長度
            //回收
            gc();
            //重新獲取回收后方庭,key的位置
            pos = ContainerHelper.binarySearch(mKeys,mSize,key);
        }
        //進(jìn)行插入操作
        mKeys = GrowingArrayUtils.insert(mKeys,mSize,pos,key)
        mValues = GrowingArrayUtils.insert(mValues,mSize,pos,value)
        mSize++;
    }
}

二分查找

public static int binarySearch(int[] mKeys,int mSize,int key){
     int low = 0;
     int high = mSize-1;
     while(low<=high){
         int mid = (low+high)>>>2; //無符號(hào)右移1位及(除2)
         int midValue = mKeys[mid];
         if(midValue < key){
            low = mid+1;
         }else if(midValue > key){
            high = mid-1;
         }else{
            return mid;
         }
     }
     return ~low;
}

該二分查找方法,如果mkeys中存在key酱固,則返回該key所在的位置下標(biāo)械念,如果不存在則返回即將要插入的位置的坐標(biāo)的反碼。

回收

public void gc(){
    int[] tempKeys = mKeys;
    Object[] tempValues = mValues;
    int n = mSize;
    int avaliableSize = 0;
    for(int i =0;i<n;i++){
        Object value = tempValues[i];
        if(value != DELETED){
            if(i != avaliable){
                tempValues[avaliableSize] = value
                tempKeys[avaliableSize] = tempKeys[i]
                tempValues[i] = null
            }
            avaliableSize++;
        }
    }
    mGarbage = false;
    mSize = avaliableSize;
}

回收數(shù)組中的DELETED的數(shù)值

原始數(shù)組mKeys:[5,6,7,8,9]
原始數(shù)組mValues:[6,DELETED,DELETED,9,10]
mSize = 5
gc()過后的數(shù)組
數(shù)組mKeys:[5,8,9,8,9]
數(shù)組mValues:[6,9,10,null,null]
mSize = 3

插入數(shù)據(jù)(1.判斷是否需要擴(kuò)容运悲,如果先進(jìn)行擴(kuò)容 2.數(shù)據(jù)的拷貝后移 3龄减,插入數(shù)據(jù))

public Object[] insert(Object[] array,int mCurrentSize,int pos,Object value){
    if(mCurrentSize+1<=array.length()){//容量夠了,不需要進(jìn)行擴(kuò)容
        //先進(jìn)行數(shù)據(jù)的copy后移
        System.arrayCopy(array,pos,array,pos+1,mCurrentSize-pos);
        //插入數(shù)據(jù)
        array[pos] = value
        return array;
    }else{//數(shù)組容量不夠
        Object[] newArray = ArrayUtils.newUnpaddedArray(getSize(mCurrentSize))
        //先拷貝前面的數(shù)據(jù)
        System.arrayCopy(array,0,newArray,0,pos);
        //插入數(shù)據(jù)
        newArray[pos] = value;
        //拷貝后面的數(shù)據(jù)
        System.arrayCopy(array,pos,newArray,pos+1,mSize-pos);
        return newArray;
    }
}

public int getSize(int mCurrentSize){
    return mCurrentSize<=4?8:mCurrentSize*2;
}

插入數(shù)據(jù)的數(shù)組變化:

原始數(shù)組
mKeys:0 0 0 0 0 0 0 0 0 0
mValues:null null null null null null null null null null
mSize = 0

現(xiàn)在調(diào)用方法:put(3,perosn1)
通過二分查找返回的pos = -1
故直接插入到0位置
插入后數(shù)組:
mKeys:3 0 0 0 0 0 0 0 0 0
mValues:person1 null null null null null null null null null
mSize = 1

現(xiàn)在調(diào)用方法:put(7,perosn2)
通過二分查找返回的pos = -2
故直接插入到1位置
插入后數(shù)組:
mKeys:3 7 0 0 0 0 0 0 0 0
mValues:person1 perosn2 null null null null null null null null
mSize = 2

現(xiàn)在調(diào)用方法:put(5,perosn3)
通過二分查找返回的pos = -2
故先將1位置及以后的后移班眯,再在1位置插入person3
插入后數(shù)組:
mKeys:3 5 7 0 0 0 0 0 0 0
mValues:person1 perosn3 perosn3 null null null null null null null
mSize = 3

刪除操作

public void delete(int key){
     int pos = ContainerHelper.binarySearch(mkeys,mSize,key);
     if(pos >=0){
        mValues[pos] = DELETED;
        mGarbage = true;
     }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末希停,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子署隘,更是在濱河造成了極大的恐慌宠能,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磁餐,死亡現(xiàn)場(chǎng)離奇詭異违崇,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诊霹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門羞延,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脾还,你說我怎么就攤上這事伴箩。” “怎么了鄙漏?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵嗤谚,是天一觀的道長棺蛛。 經(jīng)常有香客問我,道長呵恢,這世上最難降的妖魔是什么鞠值? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮渗钉,結(jié)果婚禮上彤恶,老公的妹妹穿的比我還像新娘。我一直安慰自己鳄橘,他們只是感情好声离,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瘫怜,像睡著了一般术徊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鲸湃,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天赠涮,我揣著相機(jī)與錄音,去河邊找鬼暗挑。 笑死笋除,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的炸裆。 我是一名探鬼主播垃它,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼烹看!你這毒婦竟也來了国拇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤惯殊,失蹤者是張志新(化名)和其女友劉穎酱吝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體土思,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掉瞳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浪漠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陕习。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖址愿,靈堂內(nèi)的尸體忽然破棺而出该镣,到底是詐尸還是另有隱情,我是刑警寧澤响谓,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布损合,位于F島的核電站省艳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嫁审。R本人自食惡果不足惜跋炕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望律适。 院中可真熱鬧辐烂,春花似錦、人聲如沸捂贿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厂僧。三九已至扣草,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颜屠,已是汗流浹背辰妙。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甫窟,地道東北人上岗。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像蕴坪,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子敬锐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容