SparseArray存储的是键值对,以int做为key,Object做为value。Sparse有稀疏、缺乏的意思。SparseArray应用场景是相对稀少的数据,通常是几百之内。java
SparseArray并不像HashMap采用一维数组+单链表和二叉树结构,而是采用两个一维数组,一个是存储key(int类型),一个是存value object。算法
private int[] mKeys; // 存储key
private Object[] mValues; // 存储value对象
private int mSize; // 记录存储键值对的数量
复制代码
mKeys和mValues读写时采用的下标是一一对应的。 数组
SparseArray在默认构造函数中指定其默认容量大小。默认为10数据结构
初始化后mSize = 0
,实例化mKeys和mValues。app
输入一个int型的key,经过二分法查找匹配的下标。若没找到对应的下标,则返回null或用户指定的默认对象。函数
key是递增存放的。二分法查找下标时,可能会返回一个负值,此时表示在mKeys中没找到对应的键。spa
public E get(int key) {
return get(key, null);
}
/** * Gets the Object mapped from the specified key, or the specified Object * if no such mapping has been made. */
@SuppressWarnings("unchecked")
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]; // 找到指定元素
}
}
复制代码
public void put(int key, E value) {
// 二分法找到key的下标
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
// 表明当前已经存在key及其对应的值,直接替换value
mValues[i] = value;
} else {
// 表示当前并不存在key,则应添加新的键值对
// i取反,获得要添加的数组位置下标。二叉查找返回的是key的“应当”存放的位置下标。
i = ~i;
if (i < mSize && mValues[i] == DELETED) {
// 原来位置上的元素已经被删掉了,直接赋值替换
mKeys[i] = key;
mValues[i] = value;
return;
}
if (mGarbage && mSize >= mKeys.length) {
// 容量不足,进行回收操做
gc();
// 从新查找目标下标
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
// 目标下标为i,将key添加进mKeys数组中
mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
// 目标下标为i,将value插入mValues数组中
mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
// 已存储的数据个数加1
mSize++;
}
}
// GrowingArrayUtils.java
public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
assert currentSize <= array.length;
if (currentSize + 1 <= array.length) {
// 当前数组容量充足,index开始的元素后移1位
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
// 容量不足,先扩容生成新的数组newArray
@SuppressWarnings("unchecked")
T[] newArray = ArrayUtils.newUnpaddedArray((Class<T>)array.getClass().getComponentType(),
growSize(currentSize));
// 将原来数组index以前的部分复制到新数组对象中
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element; // 插入元素
// 将原数组index+1以后的元素拷贝到新数组中
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}
// 扩容计算规则,当前容量小于等于4则返回8;不然返回2倍的容量
// 扩容后最小容量是8
public static int growSize(int currentSize) {
return currentSize <= 4 ? 8 : currentSize * 2;
}
复制代码
二叉查找方法ContainerHelpers.binarySearch(int[] array, int size, int value)
code
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
}
复制代码
若是没有找到输入value对应的下标,则会返回一个按位取反后的值(通常是个负值)。cdn
例如输入array是 [1,2,4,5],size是4,value是3;那么会获得2的取反值。而2
就是value的目标位置下标。对象
SparseArray并不像HashMap同样定义有最大容量是多少,最大能够达到Integer.MAX_VALUE,可能会报oom。每次扩容时若是当前容量小于5则扩容是8,不然扩容为原容量的2倍。
public static int growSize(int currentSize) {
return currentSize <= 4 ? 8 : currentSize * 2;
}
复制代码
针对上面与HashMap的比较,采用SparseArray仍是HashMap,建议根据以下需求选取: