1、SparseArray
SparseArrays map integers to Objects. Unlike a normal array of Objects,there can be gaps in the indices. It is intended to be more memory efficient than using a HashMap to map Integers to Objects, both because it avoids auto-boxing keys and its data structure doesn't rely on an extra entry object for each mapping.
上面这段话来自SparseArray类的声明,重点关注一下: 与使用HashMap将Integers映射到Objects相比,它的内存效率更高,因为它避免了自动装箱键,并且其数据结构不依赖于每个映射的额外条目对象。(这也是SparseArray的优点)
SparseArray包含三个主要成员变量:
private int[] mKeys;
private Object[] mValues;
private int mSize;
其中,mKeys用于存放键key,mValues用来存放值(泛型E)。mSize为键值对的数量。
初始容量默认为10。
先看put方法:
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方法首先在mKeys数组中通过二分查找算法,找到key在mKeys数组中的索引 i,如果i>0,即已经存在一个键值对,则直接覆盖mValues数组中的值。如果i<0,即不存在该键值对,但是由于mKeys数组是从小到大排好序的,-i为key需要插入mKeys数组中的位置,并且mkeys和mValues数组还有剩余空间并且-i索引位置的值为空,表示不需要扩容并且不需要数据迁移,则把key存放在mKeys数组的-i索引位置,value存放在mValues数组-i索引位置。
public static long[] insert(long[] array, int currentSize, int index, long element) {
assert currentSize <= array.length;
if (currentSize + 1 <= array.length) {
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}
如果,当前mKeys数组和mValues数组已满,或者-i索引位置已经有值。则通过GrowingArrayUtils.insert方法把key插入到-i索引位置,并将-i(包含)以后的key向后平移一个单位来完成key迁移。如果mKeys数组需要扩容,则调用GrowingArrayUtils.growSize方法进行扩容
public static int growSize(int currentSize) {
return currentSize <= 4 ? 8 : currentSize * 2;
}
扩容规则为:当前容量不大于4时直接扩容到8,否则双倍扩容,由于前面说过初始容量为10,所以都是双倍扩容。扩容完成后,在完成上面的key迁移过程。
再看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];
}
}
很简单,直接通过二分查找在mKeys数组中找到对应的索引,如果索引存在则直接返回mValues数组中该索引下的值。否则返回null。
2、ArrayMap
int[] mHashes;
Object[] mArray;
int mSize;
ArrayMap同样也是三个重要成员变量:1,mHashes用于存放key对象的hashcode值,该值也按照从小到大的顺序排序。2,mArray用于存放key和value对象。具提存放策略稍后讲。3,mSize为key-value键值对的数量。
同样先看put方法
@Override
public V put(K key, V value) {
final int osize = mSize;
final int hash;
int index;
if (key == null) {
hash = 0;
index = indexOfNull();
} else {
hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
index = indexOf(key, hash);
}
if (index >= 0) {
index = (index<<1) + 1;
final V old = (V)mArray[index];
mArray[index] = value;
return old;
}
index = ~index;
if (osize >= mHashes.length) {
final int n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1))
: (osize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);
final int[] ohashes = mHashes;
final Object[] oarray = mArray;
allocArrays(n);
if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
throw new ConcurrentModificationException();
}
if (mHashes.length > 0) {
if (DEBUG) Log.d(TAG, "put: copy 0-" + osize + " to 0");
System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
System.arraycopy(oarray, 0, mArray, 0, oarray.length);
}
freeArrays(ohashes, oarray, osize);
}
if (index < osize) {
if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (osize-index)
+ " to " + (index+1));
System.arraycopy(mHashes, index, mHashes, index + 1, osize - index);
System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
}
if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
if (osize != mSize || index >= mHashes.length) {
throw new ConcurrentModificationException();
}
}
mHashes[index] = hash;
mArray[index<<1] = key;
mArray[(index<<1)+1] = value;
mSize++;
return null;
}
从上面可以看出,put方法先判断key是否为空,如果为空,则hashcode为0,否则获取key的hashcode值,然后根据hashcode值在mKeys数组中找到对应的索引index,如果index>=0,则在mArray数组中2*index索引位置存放key对象,2*index+1索引位置存放value对象。如果index<0,表示没找到则-index为当前hashcode要插入到mKeys数组中的位置。如果mHashes数组需要扩容,则判断当前mSize是否大于等于8(BASE_SIZE*2),如果是则扩容为原来的1.5倍,否则判断mSize是否大于等于4,如果是则扩容为8,否则扩容为4。扩容完成后,将数据迁移到新的数组中。
get方法与SparseArray差不多。