系列文章地址:
Android容器類-ArraySet原理解析(一)
Android容器類-ArrayMap原理解析(二)
Android容器類-SparseArray原理解析(三)
Android容器類-SparseIntArray原理解析(四)
SparseArray
優化了int
到Object
鍵值對的儲存,SparseIntArray
優化了int
到int
鍵值對的儲存。android中在鍵值對儲存上的優化主要做了一下幾種型別的優化:
int
-->Object
(SparseArray)int
-->int
(SparseIntArray)int
-->boolean
(SparseBooleanArray)int
-->long
(SparseLongArray)int
-->Set
(SparseSetArray)
SparseSetArray目前在sdk中還處於hide狀態,故在做總結的時候就不分析它了。
之前已經分析過SparseArray
,本文就分析下SparseIntArray
的實現,並在最後總結下這幾種鍵值對在實現上的共同點。
繼承結構
以上為SparseIntArray
的繼承體系。SparseIntArray
只實現了Cloneable
介面,結構比較簡單。其實閱讀原始碼可以發現,SparseArray
,SparseIntArray
,SparseBooleanArray
,SparseLongArray
都只實現了Cloneable
介面。
儲存結構
以上為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
函式的實現如下:
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開始的元素整體後移一位,然後插入元素,否則先擴容,然後將元素拷貝到新的陣列中。
刪除元素
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)
,先使用二分查詢,找到key
在mKeys
的下標,如果找到即i >= 0
,則直接刪除mKeys
和mValues
指定位置的元素。
總結
SparseBooleanArray
,SparseLongArray
還沒有分析,他們的實現規則是一樣的,只是儲存的資料型別的mValues
陣列是boolean
和 long
,接下來就對SparseIntArray
,SparseBooleanArray
,SparseLongArray
進行下總結。
- 他們的設計目的是優化
int
到int
,boolean
,long
對映的儲存 - 使用
int
型別的陣列mKeys
儲存對映的鍵,使用對應型別的陣列mValues
儲存值 int
型別的鍵在儲存上是有順序的- 在查詢值時,先使用二分查詢,在
mKeys
中查詢值在mValues
中的下標,然後返回值
以上三種資料型別和SparseArray
最大的區別在於SparseArray
在刪除元素的時候會將元素設定為DELETED
,後續會有gc
的過程。
相對於使用HashMap,這樣的設計的優勢和缺點:
優勢:
- 避免
int
型別的鍵自動裝箱 - 相較於
HashMap
使用Node
,這樣的設計使用更小的儲存單元即可儲存key
到value
的對映
缺點:
- 在進行元素查詢時使用二分查詢,元素較多(谷歌給出的數字是大於1000)時,查詢效率較低
- 在進行元素的新增和刪除時,可能會頻繁進行元素的移動,執行效率可能會降低