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;//新的数组大小
}