当前位置: 首页 > news >正文

SparseArray ArrayMap

SparseArrayArrayMap 都是 Android 特有的集合类,出现在 android.util 包下。
它们的诞生原因是 Android 在内存和性能优化上的特殊需求 —— 在移动设备上,内存、GC 频率和性能都是敏感指标,所以 Google 给我们提供了比 Java 标准集合更轻量的替代方案。

它们在功能上与 Java 标准库中的 HashMap 类似,都是键值对(Key-Value)存储,但在内部实现上做了显著的改进。

一、SparseArray

SparseArray 是一种将整数映射到对象的键值对数据结构。它的设计初衷是为了替代 HashMap<Integer, Object>,特别是在键是连续或稀疏的整数时。

它在系统源码和三方库中很常见,但因没有去了解过它,以至于很长时间都不知道它是干什么用的,只知道看上去和 HashMap 很像。

特点:两个数组(key 升序排列,value 对应存储)

mKeys:   [  1,   3,   5  ]
mValues: ["A", "B", "C"]查找流程:
1. 用二分查找 mKeys 找到 key 的位置
2. 用下标去 mValues 取值

结构示意:

┌──────────────┐     ┌──────────────┐
│ mKeys  int[] │     │ mValues Obj[]│
├─────┬────────┤     ├─────┬────────┤
│  01-->0"A"   │
│  13-->1"B"   │
│  25-->2"C"   │
└─────┴────────┘     └─────┴────────┘

删除元素时不会立刻移动数组,而是标记为“DELETED”占位符,等需要时再 gc() 进行压缩

二分查找大家都懂吧,就是那个过程很简单,细节是魔鬼的算法,简单介绍下,在给定一个有序的数据结构中去查找一个元素,通过目标元素的特征与中间的元素比较确定它属于哪部分,往复循环,每次都是缩小目标数据结构的一半,直到找到元素,很好理解是不是,恩,保持你对它的第一印象就行了。

原理

SparseArray 内部使用两个并行的数组:一个用于存储键(int[] mKeys),另一个用于存储值(Object[] mValues)。当添加一个键值对时,它会根据键的大小将键插入到 mKeys 数组的正确位置,并同时将值插入到 mValues 数组的对应位置。由于数组是连续存储的,查找操作使用二分查找(binary search),其时间复杂度为 O(logn),而添加和删除操作可能需要移动数组元素,其时间复杂度为 O(n)。

作用与功能

SparseArray 避免了 HashMap 中每个键值对都需要创建一个 Entry 对象的开销。在 HashMap 中,每个 Entry 对象包含了键、值以及指向下一个 Entry 的引用,这些都会占用额外的内存。而 SparseArray 仅仅存储原始的 int 键和 Object 值,因此内存消耗更低。

Java 泛型不支持基本类型,Integer 会频繁装箱(int → Integer)和拆箱(Integer → int),增加 CPU 与内存占用。SparseArray就避免了频繁装箱/拆箱。

虽然添加和删除操作可能较慢,但对于查找操作,二分查找在数据量不大的情况下(通常在数百或数千个元素以内),其性能可以媲美甚至超过 HashMap。数据量大时,性能退化至少 50 %!

SparseArray 家族

SparseArray 只能存储 key 为 int 的数据,当我们要存储的数据 key 不为 int 类型或 value 是基本类型 怎么办?不慌,下面有请SparseArray 家族成员登场

  • SparseArray<E>
    Key = int,Value = Object,替代 HashMap<Integer, E>
  • SparseBooleanArray
    Key = int,Value = boolean,替代 HashMap<Integer, Boolean>
  • SparseIntArray
    Key = int,Value = int,替代 HashMap<Integer, Integer>
  • SparseLongArray
    Key = int,Value = long,替代 HashMap<Integer, Long>
  • LongSparseArray<E>
    Key = long,Value = Object,替代 HashMap<Long, E>

应用场景

  • 键为 int 类型:当你的键是整数时,这是首选。
  • 数据量相对较小:如果键值对的数量不多,SparseArray 的性能优势更为明显。
  • 键是稀疏的:即使键的数值范围很大,但实际使用的键数量不多,SparseArray 也能很好地工作。例如,你可能需要将视图 ID(如 R.id.my_view)映射到某个对象,这些 ID 是不连续的,但总数不多。

在安卓中常见的就是存储监听/订阅列表或存储 View 之类的。

缺点

  • 插入时会用二分查找 + 数组移动,数据量大时效率比 HashMap 低
  • 不是线程安全的
  • 删除操作延迟清理(gc() 机制)

如果只使用SparseArray 你就会发现,还是 HashMap 好用,因为SparseArray 没有像HashMap 那种以对象为 key 的存储方式,不要慌,接下来登场的是另一个安卓特有集合《ArrayMap

二、ArrayMap

ArrayMap 是一种通用型的键值对数据结构,它的设计目的是为了在键值对数量较少时,替代 HashMap 以获得更高的内存效率和更好的性能。

特点:一个 int[] mHashes(存 hashCode,升序),一个 Object[] mArray(交错存储 key 和 value)

mHashes: [100, 200, 350]
mArray:  [ "key1", "val1", "key2", "val2", "key3", "val3" ]

结构示意:

┌────────────────┐      ┌────────────────────────────────┐
│ mHashes  int[] │      │ mArray Object[]                │
├─────┬──────────┤      ├─────┬─────────────┬─────┬──────┤
│  0100-->0"key1"1"val1"│
│  1200-->2"key2"3"val2"│
│  2350-->4"key3"5"val3"│
└─────┴──────────┘      └─────┴─────────────┴─────┴──────┘

查找流程:

  1. mHashes 用二分查找 hashCode
  2. mArray 中取出对应的 key,调用 equals() 比较
  3. 如果 key 相等,返回对应的 value

查找下标的操作:

int indexOf(Object key, int hash) {// 当前数据的大小final int N = mSize;// Important fast case: if nothing is in here, nothing to look for.// 如果大小为0,说明没有数据存在,返回 -1 表示插入if (N == 0) {return ~0;}// 二分查找到下标int index = binarySearchHashes(mHashes, N, hash);// If the hash code wasn't found, then we have no entry for this key.// 返回要插入的下标位置if (index < 0) {return index;}// If the key at the returned index matches, that's what we want.// 如果返回索引处的键匹配,那就是我们想要的。index 是 0 的话,那么它的值就是1的位置。// index<<1 就等于 0 + 1if (key.equals(mArray[index<<1])) {return index;}// Search for a matching key after the index.// 遇到冲突,开放地址发解决冲突,从前往后找int end;for (end = index + 1; end < N && mHashes[end] == hash; end++) {if (key.equals(mArray[end << 1])) return end;}// Search for a matching key before the index.// 从后往前找for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {if (key.equals(mArray[i << 1])) return i;}// Key not found -- return negative value indicating where a// new entry for this key should go.  We use the end of the// hash chain to reduce the number of array entries that will// need to be copied when inserting.// 未找到是取反表示需要插入的位置return ~end;
}

原理

SparseArray 类似,ArrayMap 也是通过两个并行的数组来实现的:一个用于存储键的哈希码(int[] mHashes),另一个用于存储键值对(Object[] mArray)。mArray 数组中,键和值是交替存储的,即 mArray = {key1, value1, key2, value2, ...}。查找、添加和删除操作的实现也类似于 SparseArray,首先通过二分查找找到键的哈希码,然后检查键是否相等。

作用与功能

ArrayMap 同样避免了为每个键值对创建 Entry 对象的开销,从而大大减少了内存占用。对于少量键值对,ArrayMap 的内存消耗远低于 HashMap

在数据量较小(通常在数百个元素以内)时,ArrayMap 的查找、添加和删除操作由于其精简的内部结构和对哈希冲突的优化处理,其性能可以优于 HashMap。它不需要处理复杂的链表或红黑树结构,代码实现更轻量。

应用场景

  • 键值对数量较少ArrayMap 的性能拐点通常在数百个元素左右,当数据量超过这个范围,HashMap 的性能优势会逐渐显现。
  • 需要通用键值对存储:与 SparseArray 不同,ArrayMap 的键可以是任意对象,而不仅仅是 int 类型。
  • 性能和内存都是关键考虑因素:在内存受限的移动设备上,例如在一个 RecyclerViewAdapter 中存储少量的数据,ArrayMap 是一个很好的选择。

总的来说 ArrayMap 就是 替代 HashMap<K,V> 的轻量实现。

缺点

  • 插入/删除需要移动数组,数据量大时性能差
  • 不是线程安全的
  • 数据量大时性能不如 HashMap

三、总结

  • SparseArray 系列:Key 是原始数值类型(int、long),替代 HashMap<Integer, T>,避免装箱节省内存。
  • ArrayMap:Key 可以是任意对象,替代 HashMap<K,V>,小数据量节省内存。
  • 共同点
    • 数据量少时比 HashMap 更省内存
    • 查找是二分法 + 数组结构
    • 插入删除需要移动数据,性能不适合超大数据集
http://www.dtcms.com/a/329764.html

相关文章:

  • Activity和Fragment生命周期
  • Spring进阶(八股篇)
  • 栈和队列详解
  • LeetCode刷题记录----437.路径总和Ⅲ(medium)
  • 学习:JS进阶[10]内置构造函数
  • HunyuanVideo-Avatar:为多个角色制作高保真音频驱动的人体动画
  • C++哈希进阶-位图
  • 计算机网络技术-知识篇(Day.1)
  • java14学习笔记-打包工具 (Incubator)
  • MoonBit Perals Vol.05: 函数式里的依赖注入:Reader Monad
  • JPrint免费的Web静默打印控件:PDF打印中文乱码异常解决方案
  • 什么是JSP和Servlet以及二者的关系
  • window显示驱动开发—多平面覆盖 VidPN 呈现
  • MVCC底层实现原理
  • Flask入门:从零搭建Web服务器
  • 雅思大作文笔记
  • iOS 签名证书在版本迭代和iOS上架中的全流程应用
  • Docker 在 Linux 中的额外资源占用分析
  • 智汇河套,量子“风暴”:量子科技未来产业发展论坛深度研讨加速产业成果转化
  • 信息学奥林匹克竞赛(NOI/NOIP/CSP) 学习进度自查表
  • 使用 Python Selenium 和 Requests 实现歌曲网站批量下载实战
  • 嵌入式学习 day50 IMX6ULL裸机开发 - 时钟
  • Linux 5.15.189-rt87 实时内核安装 NVIDIA 显卡驱动
  • 手机充电线贴标机如何使用
  • 内存可见性和伪共享问题
  • php7 太空船运算符
  • 在实验室连接地下车库工控机及其数据采集设备
  • 《零基础入门AI:深度学习中的视觉处理(卷积神经网络(CNN)学习)》
  • Github desktop介绍(GitHub官方推出的一款图形化桌面工具,旨在简化Git和GitHub的使用流程)
  • SpringMVC 6+源码分析(六)参数处理