SparseArray源碼解析

1.SparseArray簡介  

    SparseArray的主要作用是將Integers映射到Objects,相當於Map<Integer,Object>。當需要將Integers映射到Objects時,SparseArray比HashMap更高效。因爲SparseArray避免自動裝箱keys,並且它的數據結構不依賴於外部的Entry。
    SparseArray是在一個數組結構中維護它的映射關係,通過二分查找來查找key。SparseArray不適合包含大量數據的場景,因爲添加和刪除一個元素時,需要先二分查找,找到key所對應的索引index,然後在數組中插入和刪除一個value。如果需要包含大量的數據,建議選用HashMap。
    爲了更好的性能,SparseArray在移除元素的時候,包含了一個優化:在刪除元素的時候,不立即將該元素移除,而是標記該位置的元素被刪除了。這樣該key位置上的entry是可以被複用,當需要壓縮數組的時候,將這些被標記爲刪除的entry全部被回收。數組壓縮可以發生在任何時候,例如數組需要增大或是entry值需要回收時。
    可以通過keyAt(int)和ValueAt(int)來迭代它的選項。迭代keys可以通過keyAt(int)函數,迭代values可以通過valueAt(int)函數來獲取。

2.SparseArray源碼分析

//實現了Cloneable接口
public class SparseArray<E> implements Cloneable {
    private static final Object DELETED = new Object();  //DELETED對象,用來指示被刪除的對象
    private boolean mGarbage = false;//指示是否垃圾回收
    private int[] mKeys;//保存key值的數組
    private Object[] mValues;//保存value值的數組
    private int mSize;//當前SparseArray的大小
//構造函數,默認元素數量爲10個
public SparseArray() {
        this(10);
    }

 //帶Capacity的構造函數,當Capacity爲0時,不限定數組的大小
 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;
    }
}

//clone接口,實現key數組和value數組的拷貝
 public SparseArray<E> clone() {
        SparseArray<E> clone = null;
        try {
            clone = (SparseArray<E>) super.clone();
            clone.mKeys = mKeys.clone();
            clone.mValues = mValues.clone();
        } catch (CloneNotSupportedException cnse) {
            /* ignore */
        }
        return clone;
    }
//從指定的key值獲取映射的Object
public E get(int key) {
        return get(key, null);
    }

public E get(int key, E valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//二分查找key值對應的索引值i
       //當索引值i小於0,或者值是被標記爲刪除時,返回valueIfKeyNotFound
    if (i < 0 || mValues[i] == DELETED) {
            return valueIfKeyNotFound;
        } else {
            return (E) mValues[i];
        }
    }

//將指定key對應的Value對象標記爲DELETED刪除
public void delete(int key) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i >= 0) {
            if (mValues[i] != DELETED) {
                mValues[i] = DELETED;
                mGarbage = true;
            }
        }
    }

//與delete方法具有相同的功能
public void remove(int key) {
        delete(key);
}

//移除指定索引的Value對象
public void removeAt(int index) {
        if (mValues[index] != DELETED) {
            mValues[index] = DELETED;
            mGarbage = true;
        }
    }

//回收被標記DELETE的對象,將數組向前壓縮
private void gc() {
        int n = mSize;
        int o = 0;
        int[] keys = mKeys;
        Object[] values = mValues;
        for (int i = 0; i < n; i++) {
            Object val = values[i];
             //如果val不等於DELETED對象,i和o都整體往後移動,否則只讓i後移,這樣DELETE對象會被後面的對象覆蓋,DELETED對象後面的對象都整體向前移動
            if (val != DELETED) {
                if (i != o) {
                    keys[o] = keys[i];
                    values[o] = val;
                    values[i] = null;
                }
                o++;
            }
        }
        mGarbage = false;
        mSize = o;//更新數組壓縮後的大小
    }
//將一個<key,value>對放入到數組中
public void put(int key, E value) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//二分查找key值對應的索引

        if (i >= 0) {
            //1.如果索引值大於0,說明數組已經存在該key了,直接更新key對應的value值
            mValues[i] = value;
        } else {
            i = ~i;//沒有找到對應的索引,說明該<key,value>是新插入的,
            //2.如果索引i小於數組的大小,並且該索引i對應value是DELETED對象,說明該key對應的對象被移除過,現在可以複用該key,並且更新對應的value對象。
            if (i < mSize && mValues[i] == DELETED) {
                mKeys[i] = key;
                mValues[i] = value;
                return;
            }
            //如果存儲空間不足,並且可以壓縮數組空間,則先壓縮數組空間,並且重新生成新的索引,因爲壓縮數組之後,數組元素的位置可能會發生變化,需要重新生成索引。
            if (mGarbage && mSize >= mKeys.length) {
                gc();
                // Search again because indices may have changed.
                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
            }
            //3.將key和value值分別插入key數組和value數組
            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;
        }
    }

// 返回數組的實際大小
 public int size() {
        if (mGarbage) {
            gc();
        }
        return mSize;
    }
 //返回指定索引i對於的key值
 public int keyAt(int index) {
        if (mGarbage) {
            gc();
        }
        return mKeys[index];
    }

//返回指定索引對於的value對象
 public E valueAt(int index) {
        if (mGarbage) {
            gc();
        }

        return (E) mValues[index];
    }
//在指定的索引i上,設置value值
 public void setValueAt(int index, E value) {
        if (mGarbage) {
            gc();
        }
        mValues[index] = value;
    }

//返回指定key值對應的索引i,通過二分查找返回對於的索引
public int indexOfKey(int key) {
        if (mGarbage) {
            gc();
        }
        return ContainerHelpers.binarySearch(mKeys, mSize, key);
    }

//返回指定value對象對應的索引i,通過線性查找
public int indexOfValue(E value) {
        if (mGarbage) {
            gc();
        }

        for (int i = 0; i < mSize; i++)
            if (mValues[i] == value)
                return i;

        return -1;
    }

//從SparseArray數組中清除key-value映射對
 public void clear() {
        int n = mSize;
        Object[] values = mValues;

        for (int i = 0; i < n; i++) {
            values[i] = null;
        }

        mSize = 0;
        mGarbage = false;
    }

//將一個<key,value>對添加到數組的末尾
public void append(int key, E value) {
        //1.key值小於最大的值,不能添加到數組的末尾,需要用put的方法將<key,value>存入
        if (mSize != 0 && key <= mKeys[mSize - 1]) {
            put(key, value);
            return;
        }

       //2.如果數組容量不夠,需要先壓縮數組,回收被刪除的對象。
        if (mGarbage && mSize >= mKeys.length) {
            gc();
        }
        //3.將<key,value>添加到數組的末尾
        mKeys = GrowingArrayUtils.append(mKeys, mSize, key);
        mValues = GrowingArrayUtils.append(mValues, mSize, value);
        mSize++;
    }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章