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;//新的數組大小
}