Android 集合探秘ArrayMap 与 SparseArray 的奇妙之旅引言集合世界的新成员在 Android 开发的奇妙世界里我们常常与各种集合类打交道。就像在一个大型超市里不同的商品需要存放在不同的货架区域一样数据也需要根据其特点和使用场景被妥善地放置在合适的数据结构中。最常见的 HashMap它就像一个大型的综合货架能容纳各种类型的 “商品”键值对通过哈希算法可以快速地找到你想要的 “商品”查找速度非常快适用于大规模数据的存储和快速检索。但今天我们要聚焦在两个不太一样的 “货架” 上 ——ArrayMap 和 SparseArray。它们在 Android 开发中有着独特的地位就像是超市里那些专门为特定商品设置的特色货架。ArrayMap 和 SparseArray 是为了应对 Android 系统中一些特殊的需求而诞生的它们在某些场景下比传统的集合类表现得更加出色。那为什么在已经有了 ArrayMap 的情况下还需要设计 SparseArray 呢它们之间到底有什么区别和联系这就像是超市里的两种特色货架看起来有些相似却又各有各的用途接下来就让我们一起深入探究一下。一、ArrayMap通用的内存优化能手一结构与原理ArrayMap 就像是一个精心整理的小型仓库虽然规模不大但布局十分巧妙。它内部使用了两个数组来存储数据一个是int[] mHashes数组用来存储所有键key的哈希值并且这些哈希值是按照从小到大的顺序排列的另一个是Object[] mArray数组它的长度是mHashes数组的两倍以交错的方式存储键值对即第 0 个位置存储第一个键第 1 个位置存储第一个值第 2 个位置存储第二个键第 3 个位置存储第二个值以此类推 。当我们向 ArrayMap 中插入一个键值对时它会先计算键的哈希值然后通过二分查找法在mHashes数组中寻找合适的插入位置。二分查找法就像是在一本按字母顺序排列的字典中查找单词通过不断地将查找范围缩小一半能快速地找到目标位置。如果找到了相同哈希值的位置还会进一步比较键是否相等以确定是更新值还是插入新的键值对。在查找元素时同样是先计算键的哈希值利用二分查找在mHashes数组中定位找到哈希值对应的位置后再在mArray数组中获取对应的值。这种设计使得 ArrayMap 在数据量较小时能够高效地进行插入、删除和查找操作并且由于它避免了像 HashMap 那样为每个键值对创建单独的节点对象大大节省了内存空间。二适用场景ArrayMap 适用于存储少量对象类型键值对的场景。比如在一个简单的用户信息管理模块中我们可能只需要存储用户的姓名、年龄、性别等少量信息这时使用 ArrayMap 就非常合适。它可以将这些信息以键值对的形式存储起来并且占用的内存空间比 HashMap 要小很多。再比如在一个配置项管理系统中我们可能会存储一些系统的配置参数如是否开启音效、是否显示通知等这些配置项的数量通常不会太多使用 ArrayMap 可以有效地管理这些配置信息并且在需要频繁读取这些配置项时ArrayMap 的遍历操作效率也比较高。因为它是顺序遍历数组对缓存更加友好能够快速地获取到每个键值对。二、SparseArray整型键的专属利器一独特设计SparseArray 则像是一个专门为整型 “商品” 打造的精致小货架有着别具一格的设计。它内部使用了两个数组来存储数据一个是int[] mKeys数组专门用来存储整型的键key这些键是按照从小到大的顺序排列的另一个是Object[] mValues数组用于存储与键相对应的值value 。当我们往 SparseArray 中添加一个键值对时它会先通过二分查找法在mKeys数组中查找合适的插入位置以保证mKeys数组始终有序。二分查找法就像是在一本按页码顺序排列的图书中查找特定页码每次都能将查找范围缩小一半从而快速定位。找到位置后就在mKeys数组的相应位置插入键同时在mValues数组的对应位置插入值。在查找元素时同样是利用二分查找在mKeys数组中找到键的位置然后在mValues数组中取出对应的值。这种设计最大的特点就是避免了装箱操作因为它直接使用基本数据类型 int 作为键而不需要像 HashMap 那样将 int 类型的键装箱成 Integer 对象大大节省了内存和 CPU 资源。 而且由于键是有序存储的在某些场景下比如需要按顺序遍历键值对时SparseArray 的表现会更加出色。二应用场景举例SparseArray 适用于键为整型且数据量较小的场景。在 Android 开发中一个常见的应用场景是将视图 IDView ID映射到对应的视图对象。每个视图在布局文件中都有一个唯一的整型 ID例如R.id.button1我们可以使用 SparseArray 来存储这些视图 ID 和对应的视图对象。SparseArrayViewviewSparseArraynewSparseArray();viewSparseArray.put(R.id.button1,findViewById(R.id.button1));viewSparseArray.put(R.id.textView,findViewById(R.id.textView));// 查找视图ViewbuttonviewSparseArray.get(R.id.button1);这样做不仅比使用 HashMap 更加节省内存而且在查找视图时利用 SparseArray 的二分查找特性速度也相当快。再比如在一个简单的游戏开发中我们可能需要存储玩家在不同关卡的得分情况关卡编号通常是整型的。这时使用 SparseArray 来存储关卡编号和对应的得分就非常合适。SparseArrayIntegerscoreSparseArraynewSparseArray();scoreSparseArray.put(1,100);scoreSparseArray.put(2,200);// 获取第二关的得分intscorescoreSparseArray.get(2);在这个例子中如果使用 HashMap会因为将整型的关卡编号装箱成 Integer 对象而浪费内存而 SparseArray 则可以避免这个问题同时在数据量不大的情况下其查找和插入操作的性能也能满足需求。三、两者对比差异决定选择现在我们已经了解了 ArrayMap 和 SparseArray 各自的特点和适用场景接下来就来深入对比一下它们看看在不同的维度下它们各自有着怎样的表现。一内存占用在内存占用方面SparseArray 堪称 “内存小能手” 。因为它直接使用 int 类型数组存储键避免了装箱操作也就没有了将基本数据类型转换为对象时所带来的额外内存开销。而且它没有像 HashMap 那样每个键值对都需要创建一个单独的 Entry 对象大大减少了内存的使用。相比之下ArrayMap 虽然也通过两个数组来存储数据避免了像 HashMap 那样为每个键值对创建单独节点对象的开销但当键为对象类型时还是会存在一定的内存占用因为对象本身需要占用内存空间并且它还需要一个数组来存储键的哈希值。所以在内存占用上SparseArray 要优于 ArrayMap尤其是当键为整型且数据量较大时这种优势更加明显。二查找效率从查找效率来看ArrayMap 和 SparseArray 都采用了二分查找法来定位元素。当数据量较小时两者的查找效率相差不大都能快速地找到目标元素时间复杂度均为 O (log n) 。然而当数据量逐渐增大时由于 ArrayMap 需要先计算键的哈希值再通过哈希值在哈希数组中进行二分查找这个过程会带来一定的时间开销。而 SparseArray 直接在有序的整型键数组中进行二分查找相对来说更加直接。不过总体而言在数据量不是特别巨大的情况下它们的查找效率都能满足大多数场景的需求。三插入删除效率在插入和删除操作上两者的表现都不算特别出色 。ArrayMap 插入新元素时需要在哈希数组中找到合适的位置插入哈希值同时在键值数组中插入对应的键值对并且可能需要移动数组元素来保持有序性时间复杂度为 O (n)。删除元素时同样需要移动数组元素效率较低。SparseArray 插入元素时要在有序的键数组中找到合适位置插入键和对应的值可能需要移动数组元素时间复杂度为 O (n) 。不过SparseArray 有一个特殊的延迟回收机制删除元素时只是将其标记为 “已删除”并不会立即移动数组元素而是在后续插入操作或主动调用gc()方法时才进行数组压缩这在一定程度上减少了频繁移动数组元素带来的开销所以在频繁插入和删除操作的场景下SparseArray 的表现会略好于 ArrayMap。四适用数据量ArrayMap 和 SparseArray 都更适合存储小到中等规模的数据一般建议数据量在千级以内 。当数据量超过这个范围时随着数组的不断扩容以及插入删除操作导致的数组元素频繁移动它们的性能会逐渐下降此时使用 HashMap 可能是更好的选择。因为 HashMap 在大数据量下虽然存在哈希冲突和扩容等问题但它的平均查找时间复杂度为 O (1)在查找性能上更具优势。五键类型键类型的选择是区分 ArrayMap 和 SparseArray 的关键因素 。ArrayMap 可以存储任意类型的键包括 null这使得它在处理各种类型的数据时非常灵活。比如我们可以用它来存储字符串类型的键值对如配置参数信息等。而 SparseArray 只能存储 int 类型的键这就限制了它的使用场景只能在键为整型的情况下发挥其优势比如存储视图 ID 和对应的视图对象。四、总结各有所长按需而用通过前面的深入分析我们对 ArrayMap 和 SparseArray 有了全面的了解。ArrayMap 像是一位通用的 “内存优化大师”在数据量较小且键为对象类型时凭借其巧妙的双数组结构和独特的哈希值管理方式能高效地存储和管理数据节省内存的同时还能保证一定的操作效率特别适合存储配置信息、少量的用户数据等场景。而 SparseArray 则是为整型键值对量身定制的 “专属专家”它利用有序的整型键数组和对应的值数组避免了装箱操作在内存占用上表现出色尤其是在键为整型且数据量不大的情况下如存储视图 ID 与视图对象的映射、游戏关卡得分等场景有着独特的优势。在实际的 Android 开发中我们就像在一个充满选择的 “数据结构超市” 里挑选最合适的工具。当面对不同的业务需求时要综合考虑数据量大小、键的类型、内存占用以及操作效率等因素 选择最适合的集合类。比如当数据量较大时HashMap 凭借其高效的查找性能可能是更好的选择但如果对内存非常敏感且数据量较小那么 ArrayMap 和 SparseArray 就能发挥它们的特长帮助我们优化应用的性能和内存管理。希望大家在今后的开发中都能巧妙地运用这些集合类打造出更加高效、流畅的 Android 应用。
Android 集合探秘:ArrayMap 与 SparseArray 的奇妙之旅
发布时间:2026/6/16 12:00:32
Android 集合探秘ArrayMap 与 SparseArray 的奇妙之旅引言集合世界的新成员在 Android 开发的奇妙世界里我们常常与各种集合类打交道。就像在一个大型超市里不同的商品需要存放在不同的货架区域一样数据也需要根据其特点和使用场景被妥善地放置在合适的数据结构中。最常见的 HashMap它就像一个大型的综合货架能容纳各种类型的 “商品”键值对通过哈希算法可以快速地找到你想要的 “商品”查找速度非常快适用于大规模数据的存储和快速检索。但今天我们要聚焦在两个不太一样的 “货架” 上 ——ArrayMap 和 SparseArray。它们在 Android 开发中有着独特的地位就像是超市里那些专门为特定商品设置的特色货架。ArrayMap 和 SparseArray 是为了应对 Android 系统中一些特殊的需求而诞生的它们在某些场景下比传统的集合类表现得更加出色。那为什么在已经有了 ArrayMap 的情况下还需要设计 SparseArray 呢它们之间到底有什么区别和联系这就像是超市里的两种特色货架看起来有些相似却又各有各的用途接下来就让我们一起深入探究一下。一、ArrayMap通用的内存优化能手一结构与原理ArrayMap 就像是一个精心整理的小型仓库虽然规模不大但布局十分巧妙。它内部使用了两个数组来存储数据一个是int[] mHashes数组用来存储所有键key的哈希值并且这些哈希值是按照从小到大的顺序排列的另一个是Object[] mArray数组它的长度是mHashes数组的两倍以交错的方式存储键值对即第 0 个位置存储第一个键第 1 个位置存储第一个值第 2 个位置存储第二个键第 3 个位置存储第二个值以此类推 。当我们向 ArrayMap 中插入一个键值对时它会先计算键的哈希值然后通过二分查找法在mHashes数组中寻找合适的插入位置。二分查找法就像是在一本按字母顺序排列的字典中查找单词通过不断地将查找范围缩小一半能快速地找到目标位置。如果找到了相同哈希值的位置还会进一步比较键是否相等以确定是更新值还是插入新的键值对。在查找元素时同样是先计算键的哈希值利用二分查找在mHashes数组中定位找到哈希值对应的位置后再在mArray数组中获取对应的值。这种设计使得 ArrayMap 在数据量较小时能够高效地进行插入、删除和查找操作并且由于它避免了像 HashMap 那样为每个键值对创建单独的节点对象大大节省了内存空间。二适用场景ArrayMap 适用于存储少量对象类型键值对的场景。比如在一个简单的用户信息管理模块中我们可能只需要存储用户的姓名、年龄、性别等少量信息这时使用 ArrayMap 就非常合适。它可以将这些信息以键值对的形式存储起来并且占用的内存空间比 HashMap 要小很多。再比如在一个配置项管理系统中我们可能会存储一些系统的配置参数如是否开启音效、是否显示通知等这些配置项的数量通常不会太多使用 ArrayMap 可以有效地管理这些配置信息并且在需要频繁读取这些配置项时ArrayMap 的遍历操作效率也比较高。因为它是顺序遍历数组对缓存更加友好能够快速地获取到每个键值对。二、SparseArray整型键的专属利器一独特设计SparseArray 则像是一个专门为整型 “商品” 打造的精致小货架有着别具一格的设计。它内部使用了两个数组来存储数据一个是int[] mKeys数组专门用来存储整型的键key这些键是按照从小到大的顺序排列的另一个是Object[] mValues数组用于存储与键相对应的值value 。当我们往 SparseArray 中添加一个键值对时它会先通过二分查找法在mKeys数组中查找合适的插入位置以保证mKeys数组始终有序。二分查找法就像是在一本按页码顺序排列的图书中查找特定页码每次都能将查找范围缩小一半从而快速定位。找到位置后就在mKeys数组的相应位置插入键同时在mValues数组的对应位置插入值。在查找元素时同样是利用二分查找在mKeys数组中找到键的位置然后在mValues数组中取出对应的值。这种设计最大的特点就是避免了装箱操作因为它直接使用基本数据类型 int 作为键而不需要像 HashMap 那样将 int 类型的键装箱成 Integer 对象大大节省了内存和 CPU 资源。 而且由于键是有序存储的在某些场景下比如需要按顺序遍历键值对时SparseArray 的表现会更加出色。二应用场景举例SparseArray 适用于键为整型且数据量较小的场景。在 Android 开发中一个常见的应用场景是将视图 IDView ID映射到对应的视图对象。每个视图在布局文件中都有一个唯一的整型 ID例如R.id.button1我们可以使用 SparseArray 来存储这些视图 ID 和对应的视图对象。SparseArrayViewviewSparseArraynewSparseArray();viewSparseArray.put(R.id.button1,findViewById(R.id.button1));viewSparseArray.put(R.id.textView,findViewById(R.id.textView));// 查找视图ViewbuttonviewSparseArray.get(R.id.button1);这样做不仅比使用 HashMap 更加节省内存而且在查找视图时利用 SparseArray 的二分查找特性速度也相当快。再比如在一个简单的游戏开发中我们可能需要存储玩家在不同关卡的得分情况关卡编号通常是整型的。这时使用 SparseArray 来存储关卡编号和对应的得分就非常合适。SparseArrayIntegerscoreSparseArraynewSparseArray();scoreSparseArray.put(1,100);scoreSparseArray.put(2,200);// 获取第二关的得分intscorescoreSparseArray.get(2);在这个例子中如果使用 HashMap会因为将整型的关卡编号装箱成 Integer 对象而浪费内存而 SparseArray 则可以避免这个问题同时在数据量不大的情况下其查找和插入操作的性能也能满足需求。三、两者对比差异决定选择现在我们已经了解了 ArrayMap 和 SparseArray 各自的特点和适用场景接下来就来深入对比一下它们看看在不同的维度下它们各自有着怎样的表现。一内存占用在内存占用方面SparseArray 堪称 “内存小能手” 。因为它直接使用 int 类型数组存储键避免了装箱操作也就没有了将基本数据类型转换为对象时所带来的额外内存开销。而且它没有像 HashMap 那样每个键值对都需要创建一个单独的 Entry 对象大大减少了内存的使用。相比之下ArrayMap 虽然也通过两个数组来存储数据避免了像 HashMap 那样为每个键值对创建单独节点对象的开销但当键为对象类型时还是会存在一定的内存占用因为对象本身需要占用内存空间并且它还需要一个数组来存储键的哈希值。所以在内存占用上SparseArray 要优于 ArrayMap尤其是当键为整型且数据量较大时这种优势更加明显。二查找效率从查找效率来看ArrayMap 和 SparseArray 都采用了二分查找法来定位元素。当数据量较小时两者的查找效率相差不大都能快速地找到目标元素时间复杂度均为 O (log n) 。然而当数据量逐渐增大时由于 ArrayMap 需要先计算键的哈希值再通过哈希值在哈希数组中进行二分查找这个过程会带来一定的时间开销。而 SparseArray 直接在有序的整型键数组中进行二分查找相对来说更加直接。不过总体而言在数据量不是特别巨大的情况下它们的查找效率都能满足大多数场景的需求。三插入删除效率在插入和删除操作上两者的表现都不算特别出色 。ArrayMap 插入新元素时需要在哈希数组中找到合适的位置插入哈希值同时在键值数组中插入对应的键值对并且可能需要移动数组元素来保持有序性时间复杂度为 O (n)。删除元素时同样需要移动数组元素效率较低。SparseArray 插入元素时要在有序的键数组中找到合适位置插入键和对应的值可能需要移动数组元素时间复杂度为 O (n) 。不过SparseArray 有一个特殊的延迟回收机制删除元素时只是将其标记为 “已删除”并不会立即移动数组元素而是在后续插入操作或主动调用gc()方法时才进行数组压缩这在一定程度上减少了频繁移动数组元素带来的开销所以在频繁插入和删除操作的场景下SparseArray 的表现会略好于 ArrayMap。四适用数据量ArrayMap 和 SparseArray 都更适合存储小到中等规模的数据一般建议数据量在千级以内 。当数据量超过这个范围时随着数组的不断扩容以及插入删除操作导致的数组元素频繁移动它们的性能会逐渐下降此时使用 HashMap 可能是更好的选择。因为 HashMap 在大数据量下虽然存在哈希冲突和扩容等问题但它的平均查找时间复杂度为 O (1)在查找性能上更具优势。五键类型键类型的选择是区分 ArrayMap 和 SparseArray 的关键因素 。ArrayMap 可以存储任意类型的键包括 null这使得它在处理各种类型的数据时非常灵活。比如我们可以用它来存储字符串类型的键值对如配置参数信息等。而 SparseArray 只能存储 int 类型的键这就限制了它的使用场景只能在键为整型的情况下发挥其优势比如存储视图 ID 和对应的视图对象。四、总结各有所长按需而用通过前面的深入分析我们对 ArrayMap 和 SparseArray 有了全面的了解。ArrayMap 像是一位通用的 “内存优化大师”在数据量较小且键为对象类型时凭借其巧妙的双数组结构和独特的哈希值管理方式能高效地存储和管理数据节省内存的同时还能保证一定的操作效率特别适合存储配置信息、少量的用户数据等场景。而 SparseArray 则是为整型键值对量身定制的 “专属专家”它利用有序的整型键数组和对应的值数组避免了装箱操作在内存占用上表现出色尤其是在键为整型且数据量不大的情况下如存储视图 ID 与视图对象的映射、游戏关卡得分等场景有着独特的优势。在实际的 Android 开发中我们就像在一个充满选择的 “数据结构超市” 里挑选最合适的工具。当面对不同的业务需求时要综合考虑数据量大小、键的类型、内存占用以及操作效率等因素 选择最适合的集合类。比如当数据量较大时HashMap 凭借其高效的查找性能可能是更好的选择但如果对内存非常敏感且数据量较小那么 ArrayMap 和 SparseArray 就能发挥它们的特长帮助我们优化应用的性能和内存管理。希望大家在今后的开发中都能巧妙地运用这些集合类打造出更加高效、流畅的 Android 应用。