SparseArray,即稀疏数组,作为一种数据结构,拥有比HashMap更高的执行效率(HashMap的装箱操作使其比SpaseArray慢,如果不考虑装箱拆箱,HashMap效率会比SparseArray高),且SparseArray对内存的使用比HashMap要少,在某些情况下,SparseArray是一种较优选择。SparseArray源码:
private int[] mKeys;
private Object[] mValues;
private int mSize;
public SparseArray() {
this(10);
}
public SparseArray(int initialCapacity){}
SparseArray维护两个数组,一个int[]保存key,Object[]保存value,key数组对应的i位置也就是对的value值在所value数据的i位置。SparseArray默认构造大小为10,也支持自定义大小的初始化。
private static final Object DELETED = new Object();
private boolean mGarbage = false;
public void remove(int key) {
delete(key);
}
public void delete(int key) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
if (mValues[i] != DELETED) {
mValues[i] = DELETED;
mGarbage = true;
}
}
}
先看的是SparseArray的delete和remove操作,在根据key移除相应value的时候,value[]数组并没有将对应的value彻底删除,而是将其引用DELETED , 表示此位置有空位,可以插入新值。 并将mGarbarge 置为true,用以提示数组存在是空间,以便之后的gc()使用。 而根据key的位置删除value,需要先定位key的位置,SparseArray的key值保证升序排序,因此定位使用二分查找
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
}
经典的二分查找,不多做说明
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];
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);
}
SparseArray的gc()操作,即将value数组和key数组变得紧凑
箭头为mSize的最大索引位置,在gc后将mGarbage设置为false
public void put(int key, E value) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
mValues[i] = value;
} else {
i = ~i;
if (i < mSize && mValues[i] == DELETED) {
mKeys[i] = key;
mValues[i] = value;
return;
}
if (mGarbage && mSize >= mKeys.length) {
gc();
// Search again because indices may have changed.
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
mSize++;
}
put()首先使用二分查找定位i的位置,如果定位得到 i 且 i>=0 ,说明存在key值,此时直接更新此key对应的value值。 如果定位不到 i 的位置,将 i 取反, 看看 i 是否小于mSize 并且 key[i] 对应的 value[i] 对应的值已失效,如果满足条件,则更新 i 位置的 key 值和 value值 (和前面的差别为,前者相当于更新,更新了value,后者相当于重置,重置了key和value。且此处的更新key并不会破坏key[]的递增) ; 如果 mGarbage && mSize >= mKeys.length , 即存在失效值,且当前容量mSize超过了数组最大容量, 调用gc()是数组紧凑,并释放失效值, 此时需要再次定位 i , 因为数值位于数组中的位置已经改变。 之后使用 GrowingArrayUtils.insert(),将数组扩容,并将key和value插入相应位置。
public int size() {
if (mGarbage) {
gc();
}
return mSize;
}
返回当前容量,如果需要,则gc()
public int keyAt(int index) {
if (mGarbage) {
gc();
}
return mKeys[index];
}
public E valueAt(int index) {
if (mGarbage) {
gc();
}
return (E) mValues[index];
}
根据索引值返回相应key或value,此索引前如果有需要则gc
public void append(int key, E value) {
if (mSize != 0 && key <= mKeys[mSize - 1]) {
put(key, value);
return;
}
if (mGarbage && mSize >= mKeys.length) {
gc();
}
mKeys = GrowingArrayUtils.append(mKeys, mSize, key);
mValues = GrowingArrayUtils.append(mValues, mSize, value);
mSize++;
}
和put相差不多。
以上为全部内容,有些简陋,忘见谅(待理解更深再来改进)
此博客仅供个人笔记,如有错误之处,欢迎指出