Android-SparseArray解讀

1、SparseArray是什麼

對於Android這樣的移動設備來講,內存的大小至關重要,然而HashMap雖然很強大,但是HashMap中採用了好幾種數據結構,複雜度很高,同時浪費了空間,SparseArray就應運而生了。

SparseArray翻譯過來就是稀疏數組,採用兩個一維數組存儲key和value,其中key僅支持整形元素,這也直接省略了自動裝箱的步驟。

在於HashMap直接計算出索引不同,SparseArray是通過二分法搜索key的,因此在搜索性能上SparseArray不如HashMap高效。

2、SparseArray核心代碼註釋


    //專門用來表示閒置位置的
    private static final Object DELETED = new Object();
    private boolean mGarbage = false;//表示是不是需要進行GC

    @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
    private int[] mKeys;//存儲key的數組,key只能爲int
    @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, E)
    private Object[] mValues;//存儲value的數組,可以爲任意類型
    @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
    private int mSize;//實際使用的大小。我們聲明的數組大小並不是完全使用的
    /**
     *構造函數
     */
    public SparseArray() {
        this(10);
    }
    //初始化數組長度爲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;
    }

    //將數據添加到兩個數組對應位置
    public void put(int key, E value) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//通過二分法搜索
        if (i >= 0) {//i>=0說明搜到了,直接給對應位置賦值即可
            mValues[i] = value;
        } else {
            //否則的話計算出對應位置,這裏的二分法搜的位置有兩個,一個是0,一個是當前元素數量位置,也就是有效的後一個位置
            //打個比方,搜索數組[1,3,4,5,6,無效,無效,無效...],如果搜一個大於6的數,那麼二分法那裏返回的是第一個無效位置索引的按位取反
            //也就是~5,在下面這句話中就是~~5也就是5,如果搜一個小於1的數,返回的就是~0,在下面就是~~0.表示插入位置
            i = ~i;
            if (i < mSize && mValues[i] == DELETED) {//檢查對應位是不是沒有使用。如果沒有,就直接賦值
                mKeys[i] = key;
                mValues[i] = value;
                return;
            }
            if (mGarbage && mSize >= mKeys.length) {//如果沒有進行GC(這裏的GC是指數組的壓縮)就壓縮一下
                gc();
                // Search again because indices may have changed.
                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);//然後搜到位置
            }
            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);//底下就是ArrayCopy的操作了
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;//有用的數據集大小加1
        }
    }
    //get方法
    public E get(int key) {
        return get(key, null);
    }
    //get方法很好理解,直接使用二分法搜索key數組中key的位置,然後key的位置也對應着value的位置
    //時間複雜度log(n)。還要檢查一下搜沒搜到。
    public E get(int key, E valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i < 0 || mValues[i] == DELETED) {
            return valueIfKeyNotFound;
        } else {
            return (E) mValues[i];
        }
    }

    //刪除和查找的方式是一樣的,只不過刪除過程是對數組位置賦值爲DELETE的過程
    public void delete(int key) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        if (i >= 0) {
            if (mValues[i] != DELETED) {
                mValues[i] = DELETED;
                mGarbage = true;
            }
        }
    }

    //此函數的作用是把數組中有效元素提到數組前面,然後把原位置置爲null,這樣原位置的元素就可以被GC了
    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];
            if (val != DELETED) {
                //把key數組和value數組的有效元素移動到數組的前面,再把原位置置爲null
                if (i != o) {
                    keys[o] = keys[i];
                    values[o] = val;
                    values[i] = null;//置爲null,下一次GC就可以收集了
                }
                o++;
            }
        }

        mGarbage = false;//移動完成
        mSize = o;//新的數組大小
    }

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