这二个是Android中更轻量化的数据结构,为了在有限数据容量且限定key类型的情况下,替代HashMap节约手机内存的。下面大致讲一下。
一、ArrayMap、SparseArray的结构
ArrayMap数据结构(图片来自)
通过源码可以看出ArrayMap通过mHashes数组存储key计算的Hash值,该数组也用于二分查找。通过mArrays存储键值对,键在奇数位(index乘以2),值在偶数位(index乘以2加1)。
SparseArray结构(图片来自)
java的集合中只能存储对象,不能直接存储基本数据类型,另外因为泛型支持的原因,也只能通过对象存储,所以当我们使用基础数据类型作为key的时候是会自动有拆装箱过程的。
而数组是可以存储基本数据类型的,SparseArray的通过两个数组mKeys和mValues存储键和值,其中mKeys只能存储Int值,这样就避免了拆装箱的性能损耗。
二、性能对比
这里直接给出结论吧,在数据量小于1000的情况下,二者与HashMap在性能上对比并没有太大的差异。但是在内存上SparseArray相比HashMap要小30%左右(HashMap,ArrayMap,SparseArray源码分析及性能对比)。
三、SparseArray相比HashMap更省内存
SparseArray(稀疏数组)只存储非默认元素,而不必存储大量默认且相同的元素,而HashMap需要装箱导致创建对象需要额外的开销,即使这个对象是空值,也需要创建以分配存储空间,包括键、值和哈希值。
数据量 | SparseArray | HashMap |
---|---|---|
10 个元素 | 160 字节 | 240 字节 |
100 个元素 | 1600 字节 | 2000 字节 |
1000 个元素 | 16000 字节 | 20000 字节 |
可以看到,在数据量较少的情况下,SparseArray 的内存使用量仅为 HashMap 的一半。
四、SparseArray的GC原理
SparseArray中的删除不是真正的删除,而是伪删除,源码如下:
kotlin
private static final Object DELETED = new Object();
public void delete(int key) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
if (mValues[i] != DELETED) {
//删除的元素的value会被标记为DELETED
mValues[i] = DELETED;
mGarbage = true;
}
}
}
真正触发GC删除的操作有
再看看GC的源码如下:
kotlin
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];
//如果value不为DELETED,并且i不等于o,说明前面有值被标记为DELETED
//将位置i的键移动到位置o
//将value值赋值给values数组o的位置,values数组i的位置的值置为null
//将整个数据前移,覆盖DELETED标记的位置
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);
}
从源码中可知,如果value不为DELETED,并且i不等于o,那么说明有被删除的数据,数组中的有效数据需要前移覆盖。o的值就是真正集合的有效长度,也就是mSize。由此可知SparseArray不适用于大量数据删除的情况,因为这样会导致大量数据的位移操作,影响性能。
五、使用建议
输入Sparse会看到有以下提示的可以用,整理为以下表格
类型 | 说明 |
---|---|
SparseArray | 键为int,值可以为任意类型,Android4.0及以上 |
SparseArrayCompat | SparseArray的更低兼容版本,在androidX包中,一般情况下用SparseArray即可 |
SparseLongArray | 键为int,值为long |
LongSparseArray | 与SparseArray类似,只是键为long,值可以为任意类型,一般情况下SparseArray够用了,如果key的值超过了Int.Max那也不推荐用SparseArray了,因为二分查找太慢了 |
SparseIntArray | 键为int,值为int类型 |
SparseBooleanArray | 键为int,值为boolean类型 |