Android 内存优化之ArrayMap、SparseArray

这二个是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类型

参考内容:

内存优化之ArrayMap、SparseArray、SparseIntArray

HashMap,ArrayMap,SparseArray源码分析及性能对比

相关推荐
海绵波波1076 分钟前
聊天服务器(7)数据模块
android·服务器·adb
软件聚导航32 分钟前
uniapp 实现 ble蓝牙同时连接多台蓝牙设备,支持app、苹果(ios)和安卓手机,以及ios连接蓝牙后的一些坑
android·ios·uni-app
深海呐5 小时前
Android 最新的AndroidStudio引入依赖失败如何解决?如:Failed to resolve:xxxx
android·failed to res·failed to·failed to resol·failed to reso
解压专家6666 小时前
安卓解压软件推荐:高效处理压缩文件的实用工具
android·智能手机·winrar·7-zip
Rverdoser6 小时前
Android 老项目适配 Compose 混合开发
android
️ 邪神8 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】标题栏
android·flutter·ios·鸿蒙·reatnative
努力遇见美好的生活8 小时前
Mysql每日一题(行程与用户,困难※)
android·数据库·mysql
图王大胜10 小时前
Android Framework AMS(17)APP 异常Crash处理流程解读
android·app·异常处理·ams·crash·binderdied·讣告
ch_s_t10 小时前
电子商务网站之首页设计
android
豆 腐13 小时前
MySQL【四】
android·数据库·笔记·mysql