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源码分析及性能对比

相关推荐
李子红了时44 分钟前
阿狸舞台APP(安卓端手机EncFS解密+压缩包解压+视频播放)
android·音视频
事圆则缓1 小时前
Android 使用 Jenkins 实现 CI/CD,并用 SonarQube 建立质量门禁
android·ci/cd·jenkins
其实防守也摸鱼1 小时前
DeepSeek Harness 开源贡献手记:从 Issue 到 Merge 的完整旅程
android·数据库·学习·ai·oracle·自动化
恋猫de小郭1 小时前
Android CLI 支持 AI Agent 通过 Device Streaming 调试云真机
android·前端·flutter
素师良码19 小时前
第5篇:显示驱动必备调试工具浅析
android
维克兜率天20 小时前
【维克】配对交易的季节性:哪些品种适合长拿?
android·开发语言·笔记·python·算法·kotlin·量化
墨天梦20 小时前
B06_XML控件布局与ViewBinding
android·kotlin
晚风叙码21 小时前
MySQL 表的操作:从建表到删表,一篇讲清楚
android·mysql·oracle
事圆则缓1 天前
Android 性能优化常见工具与使用场景:从症状到证据的排查指南
android·性能优化
avi91111 天前
Unity 非后台,非自动构建,但统计报表 BuildReport ,构建自动化系统C#
android·webserver·ios打包·admin·c#后台·unity打包系统·unity自动构建