序言
SparseArray 避免了key的自动装箱,是最轻量级的存储key为int的map
变量
private boolean mGarbage = false; //当出现过删除操作,会将此标识位置为true
private int[] mKeys; //存放key的数组
private Object[] mValues; //存放value的数组
private int mSize; //对象个数
put
public void put(int key, E value) {
//通过二分法寻找在 mKeys 中 是否有 key
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
// 如果存在key,直接进行值的替换
mValues[i] = value;
} else {
i = ~i;
//如果查找到的位置,小于对象个数,并且当前位置以及被删除了,则直接赋值
if (i < mSize && mValues[i] == DELETED) {
mKeys[i] = key;
mValues[i] = value;
return;
}
//如果之前发送过删除操作 (mGarbage = true) 并且对象个数超过了key数组的长度,
//证明此时有更多的辣鸡数据,所以进行一次 ‘gc’
if (mGarbage && mSize >= mKeys.length) {
gc();
// Search again because indices may have changed.
//整理完数据后,重新计算i的位置
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
mSize++;
}
}
private void gc() {
// Log.e("SparseArray", "gc start with " + mSize);
int n = mSize;
int o = 0;
int[] keys = mKeys;
Object[] values = mValues;
for (int i = 0; i < n; i++) {
Object val = values[i];
//val 标记不为DELETED的数据,会重新将数据锁紧,不留出空隙存放null值
if (val != DELETED) {
if (i != o) {
keys[o] = keys[i];
values[o] = val;
values[i] = null;
}
o++;
}
}
//清理结束
mGarbage = false;
mSize = o;
// Log.e("SparseArray", "gc end with " + mSize);
}
public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
assert currentSize <= array.length;
//不用扩容的时候,直接将目标数组之后的数据往后复制一份,然后在目标处进行赋值
if (currentSize + 1 <= array.length) {
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
//如果需要扩容,则创建新的数组对象,将旧数据复制到新数组中进行赋值
@SuppressWarnings("unchecked")
T[] newArray = ArrayUtils.newUnpaddedArray((Class<T>)array.getClass().getComponentType(),
growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}



put流程:
- 二分法查找key的位置
- 有值直接覆盖
- 如果此时没有对应的key,则将标记为
DELETED的数据,进行覆盖,覆盖 key 与 vaule - 如果需要gc,先进行gc,然后通过
System.arraycopy进行插入数据处理
get
public E get(int key) {
return get(key, null);
}
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;
}
}
}
将values数组的值标记为DELETED,在put时进行gc操作,进行清除
总结
SparseArray 相对于 ArrayMap 来说
- 由于key值只能是int,减少了自动装箱的消耗。
- 标记
DELETED,再重新对数组进行赋值,没有经历数组拷贝。 - 没有数组的容量减少操作,数组只能进行扩容操作。
所以在数据量少,且key值为int时,SparseArray的内存消耗更少。由于其二分查找法的缘故,与 ArrayMap 一样,数据量尽量保持1000以下。