SparseIntArray原理分析

系列文章地址:
Android容器類-ArraySet原理解析(一)
Android容器類-ArrayMap原理解析(二)
Android容器類-SparseArray原理解析(三)
Android容器類-SparseIntArray原理解析(四)java

SparseArray優化了intObject鍵值對的存儲,SparseIntArray優化了intint鍵值對的存儲。android中在鍵值對存儲上的優化主要作了一下幾種類型的優化:android

  • int --> Object(SparseArray)
  • int --> int(SparseIntArray)
  • int --> boolean(SparseBooleanArray)
  • int --> long(SparseLongArray)
  • int --> Set(SparseSetArray)

SparseSetArray目前在sdk中還處於hide狀態,故在作總結的時候就不分析它了。數組

以前已經分析過SparseArray,本文就分析下SparseIntArray的實現,並在最後總結下這幾種鍵值對在實現上的共同點。微信

繼承結構

繼承結構

以上爲SparseIntArray的繼承體系。SparseIntArray只實現了Cloneable接口,結構比較簡單。其實閱讀源碼能夠發現,SparseArraySparseIntArraySparseBooleanArraySparseLongArray都只實現了Cloneable接口。ide

存儲結構

存儲結構

以上爲SparseIntArray的存儲結構,mKeys存儲的是int類型的鍵,mValues存儲的是int類型的value。函數

查找元素

// 查找鍵key在mKeys的下標
    public int indexOfKey(int key) {
        return ContainerHelpers.binarySearch(mKeys, mSize, key);
    }
    
    // 查找value在mValues的下標
    public int indexOfValue(int value) {
        for (int i = 0; i < mSize; i++)
            if (mValues[i] == value)
                return i;
        return -1;
    }
複製代碼

元素的查找分鍵查找和值查找,鍵查找使用二分查找,值查找直接使用循環遍歷。優化

添加元素

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

        if (i >= 0) {
            mValues[i] = value;
        } else {
            i = ~i;

            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;
        }
    }
複製代碼

添加元素首先使用二分查找找到key在mKeys數組的下標,也就是value在mValues數組的下標。若是ContainerHelpers.binarySearch(mKeys,mSize,key)在mKeys數組中沒有找到key,則返回key待插入位置的下標的取反,若是找到了key,則直接更新mValues對應位置的值便可。 GrowingArrayUtils.insert函數的實現以下:spa

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;
    }
複製代碼

函數的邏輯很簡單,首先斷言了currentSize <= array.length;若是array在不須要擴大容量的狀況下能夠添加一個元素,則先將待插入位置index開始的元素總體後移一位,而後插入元素,不然先擴容,而後將元素拷貝到新的數組中。.net

刪除元素

public void delete(int key) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        if (i >= 0) {
            removeAt(i);
        }
    }
    
    public void removeAt(int index) {
        System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
        System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
        mSize--;
    }
複製代碼

刪除元素主要涉及以上兩個方法,delete(int key)根據key進行刪除,removeAt(int index)刪除指定下標的元素。這兩個方法都是public,故均可以直接使用。delete(int key),先使用二分查找,找到keymKeys的下標,若是找到即i >= 0,則直接刪除mKeysmValues指定位置的元素。設計

總結

SparseBooleanArray,SparseLongArray尚未分析,他們的實現規則是同樣的,只是存儲的數據類型的mValues數組是booleanlong,接下來就對SparseIntArray,SparseBooleanArray,SparseLongArray進行下總結。

  • 他們的設計目的是優化intint, boolean ,long映射的存儲
  • 使用int類型的數組mKeys存儲映射的鍵,使用對應類型的數組mValues存儲值
  • int類型的鍵在存儲上是有順序的
  • 在查找值時,先使用二分查找,在mKeys中查找值在mValues中的下標,而後返回值

以上三種數據類型和SparseArray最大的區別在於SparseArray在刪除元素的時候會將元素設置爲DELETED,後續會有gc的過程。

相對於使用HashMap,這樣的設計的優點和缺點:

優點:

  • 避免int類型的鍵自動裝箱
  • 相較於HashMap使用Node,這樣的設計使用更小的存儲單元便可存儲keyvalue的映射

缺點:

  • 在進行元素查找時使用二分查找,元素較多(谷歌給出的數字是大於1000)時,查找效率較低
  • 在進行元素的添加和刪除時,可能會頻繁進行元素的移動,運行效率可能會下降

關注微信公衆號,最新技術乾貨實時推送

image src=
相關文章
相關標籤/搜索