一句话说透ArrayMap和SparseArray在HashMap上面的改进

286 阅读3分钟

ArrayMap 和 SparseArray 是 Android 中优化内存和性能的数据结构,专为移动端场景设计。它们在 HashMap 的基础上做了以下改进:


一、HashMap 的痛点

  1. 内存开销大

    • 每个键值对需封装成 Entry 对象(包含键、值、哈希值、指针)。
    • 自动装箱(如 int → Integer)增加内存占用。
  2. 扩容成本高:动态扩容需重建哈希表,频繁操作影响性能。

  3. 小数据量效率低:哈希表的数组稀疏性导致内存浪费(如存10个元素,数组长度可能为16)。


二、ArrayMap:用数组替代哈希表

1. 核心改进

  • 数据结构:用两个数组分别存储键和值(无 Entry 对象)。

    Object[] mHashes; // 存储键的哈希值(有序)
    Object[] mArray;  // 键值交替存储:[key1, value1, key2, value2...]
    
  • 查找方式:通过 二分查找 定位键(时间复杂度 O(log n))。

  • 内存优化

    • 避免 Entry 对象开销,内存更紧凑。
    • 扩容时直接复制数组,无需重建哈希表。

2. 适用场景

  • 小数据量(百级以内) :查找效率接近 HashMap(内存优势更明显)。
  • 键值对频繁增删:内存管理更高效,减少 GC 压力。

3. 示例代码

ArrayMap<String, Integer> arrayMap = new ArrayMap<>();
arrayMap.put("Apple", 10);
arrayMap.put("Banana", 20);
int count = arrayMap.get("Apple"); // 通过二分查找定位

三、SparseArray:针对整型键的极致优化

1. 核心改进

  • 键为 int 类型:无需装箱(直接使用 int[] 数组)。

  • 数据结构

    int[] mKeys;   // 有序存储 int 键
    Object[] mValues; // 存储值
    
  • 延迟删除机制:删除时标记为 DELETED,批量清理时再压缩数组。

  • 内存优化:无哈希表结构,无 Entry 对象,内存占用极低。

2. 适用场景

  • 键为整型(如 View 的 ID) :避免 HashMap<Integer, Object> 的自动装箱开销。
  • 频繁增删但数据量小:延迟删除提升性能。

3. 示例代码

SparseArray<String> sparseArray = new SparseArray<>();
sparseArray.put(1001, "Activity");
sparseArray.put(1002, "Fragment");
String value = sparseArray.get(1001); // 二分查找键数组

四、对比总结

数据结构优势劣势适用场景
HashMap查找快(O(1)),通用性强内存开销大,小数据量效率低大数据量、高频查找
ArrayMap内存紧凑,小数据量性能优查找慢(O(log n))小数据量、键为对象类型
SparseArray内存极致优化,整型键无装箱仅支持整型键,查找慢(O(log n))键为整型、小数据量

五、性能实测参考

  • 内存占用(存100个键值对):

    • HashMap:约 3.5KB
    • ArrayMap:约 1.2KB
    • SparseArray:约 0.8KB
  • 查找速度(千次操作耗时):

    • HashMap:~1ms
    • ArrayMap(100元素):~2ms
    • SparseArray(100元素):~2ms

六、使用建议

  1. 优先用 ArrayMap/SparseArray

    • 数据量小(<1000),键为整型或简单对象。
    • 内存敏感场景(如 Android 低端设备)。
  2. 仍用 HashMap

    • 数据量大(>1000),或需要高频查找。
    • 键为复杂对象(如 String、自定义类)。

口诀
「安卓开发内存省,ArrayMap、SparseArray显神通
小数据量用数组,整型键值更轻松
哈希表虽快内存大,按需选择才聪明!」