SparseArray存儲的是鍵值對,以int做爲key,Object做爲value。Sparse有稀疏、缺乏的意思。SparseArray應用場景是相對稀少的數據,通常是幾百之內。java
SparseArray並不像HashMap採用一維數組+單鏈表和二叉樹結構,而是採用兩個一維數組,一個是存儲key(int類型),一個是存value object。算法
private int[] mKeys; // 存儲key
private Object[] mValues; // 存儲value對象
private int mSize; // 記錄存儲鍵值對的數量
複製代碼
mKeys和mValues讀寫時採用的下標是一一對應的。 數組
SparseArray在默認構造函數中指定其默認容量大小。默認爲10數據結構
初始化後mSize = 0
,實例化mKeys和mValues。app
輸入一個int型的key,經過二分法查找匹配的下標。若沒找到對應的下標,則返回null或用戶指定的默認對象。函數
key是遞增存放的。二分法查找下標時,可能會返回一個負值,此時表示在mKeys中沒找到對應的鍵。spa
public E get(int key) {
return get(key, null);
}
/** * Gets the Object mapped from the specified key, or the specified Object * if no such mapping has been made. */
@SuppressWarnings("unchecked")
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]; // 找到指定元素
}
}
複製代碼
public void put(int key, E value) {
// 二分法找到key的下標
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
// 表明當前已經存在key及其對應的值,直接替換value
mValues[i] = value;
} else {
// 表示當前並不存在key,則應添加新的鍵值對
// i取反,獲得要添加的數組位置下標。二叉查找返回的是key的「應當」存放的位置下標。
i = ~i;
if (i < mSize && mValues[i] == DELETED) {
// 原來位置上的元素已經被刪掉了,直接賦值替換
mKeys[i] = key;
mValues[i] = value;
return;
}
if (mGarbage && mSize >= mKeys.length) {
// 容量不足,進行回收操做
gc();
// 從新查找目標下標
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
// 目標下標爲i,將key添加進mKeys數組中
mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
// 目標下標爲i,將value插入mValues數組中
mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
// 已存儲的數據個數加1
mSize++;
}
}
// GrowingArrayUtils.java
public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
assert currentSize <= array.length;
if (currentSize + 1 <= array.length) {
// 當前數組容量充足,index開始的元素後移1位
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
// 容量不足,先擴容生成新的數組newArray
@SuppressWarnings("unchecked")
T[] newArray = ArrayUtils.newUnpaddedArray((Class<T>)array.getClass().getComponentType(),
growSize(currentSize));
// 將原來數組index以前的部分複製到新數組對象中
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element; // 插入元素
// 將原數組index+1以後的元素拷貝到新數組中
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}
// 擴容計算規則,當前容量小於等於4則返回8;不然返回2倍的容量
// 擴容後最小容量是8
public static int growSize(int currentSize) {
return currentSize <= 4 ? 8 : currentSize * 2;
}
複製代碼
二叉查找方法ContainerHelpers.binarySearch(int[] array, int size, int value)
code
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
}
複製代碼
若是沒有找到輸入value對應的下標,則會返回一個按位取反後的值(通常是個負值)。cdn
例如輸入array是 [1,2,4,5],size是4,value是3;那麼會獲得2的取反值。而2
就是value的目標位置下標。對象
SparseArray並不像HashMap同樣定義有最大容量是多少,最大能夠達到Integer.MAX_VALUE,可能會報oom。每次擴容時若是當前容量小於5則擴容是8,不然擴容爲原容量的2倍。
public static int growSize(int currentSize) {
return currentSize <= 4 ? 8 : currentSize * 2;
}
複製代碼
針對上面與HashMap的比較,採用SparseArray仍是HashMap,建議根據以下需求選取: