返回

SparseArray 源码解析:深度剖析高效映射容器

Android

导言

在 Android 开发中,管理海量数据的存储和检索至关重要。SparseArray 是一种轻量级且高效的容器类,专为存储键值对映射关系而设计,其中键值类型限定为 int。相较于传统的 HashMap,SparseArray 在特定场景下表现出显著的优势,例如处理稀疏数据集合或需要快速访问 int 型键值对的情况。

SparseArray 的内部结构

SparseArray 的内部结构基于两个主要数据结构:int[] 数组和 Object[] 数组。int[] 数组存储键值,Object[] 数组存储对应的值。为了提高查找效率,SparseArray 采用散列表的技术,将键值映射到数组中的索引位置。

优势与劣势

优势:

  • 针对 int 型键值的优化,提高查找效率。
  • 内存占用小,适用于存储稀疏数据集。
  • 访问速度快,适合快速检索和更新操作。

劣势:

  • 仅支持 int 型键值,限制了其通用性。
  • 由于使用散列表,可能会出现哈希冲突,影响查找效率。
  • 在键值较多的情况下,效率可能不如 HashMap。

应用场景

SparseArray 在 Android 开发中有着广泛的应用场景,例如:

  • 存储控件 ID 与视图对象的映射。
  • 管理稀疏数组,例如地图网格或稀疏矩阵。
  • 在自定义视图中维护键值对映射,例如 RecyclerView 的 ItemView。

源码分析

1. 键值存储与检索

public void put(int key, Object value) {
    int index = indexOfKey(key);
    if (index >= 0) {
        mValues[index] = value;
    } else {
        index = mSize++;
        if (mSize >= mCapacity) {
            resize(mCapacity << 1); // 扩容
        }
        mKeys[index] = key;
        mValues[index] = value;
    }
}

public Object get(int key) {
    int index = indexOfKey(key);
    return index >= 0 ? mValues[index] : null;
}

put() 方法根据键值查找对应的索引,若存在则更新值;否则,扩容数组并存储键值对。get() 方法根据键值查找索引并返回对应值。

2. 哈希冲突处理

private int indexOfKey(int key) {
    int low = 0;
    int high = mSize - 1;

    while (low <= high) {
        int mid = (low + high) >>> 1;
        int midVal = mKeys[mid];

        if (midVal == key) {
            return mid;
        } else if (midVal < key) {
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }

    return -1;
}

indexOfKey() 方法使用二分查找算法在键值数组中查找键值,若找到则返回索引;否则,返回 -1。

优化建议

  • 在 SparseArray 中存储少量键值对时,可以考虑使用 HashMap,因为 HashMap 在这种场景下效率更高。
  • 对于键值较多且访问频繁的情况,可以使用 SparseArray 的 putAll() 和 getAll() 方法进行批量操作,以提高效率。
  • 避免频繁扩容,可以预估数据规模并预先设置合适的容量。

总结

SparseArray 是 Android 开发中一种高效的容器类,专为存储 int 型键值对映射而设计。它具有轻量级、快速查找等优点,适用于稀疏数据存储和快速访问场景。通过深入理解 SparseArray 的内部结构和使用场景,可以充分发挥其优势,打造更高效、更稳定的 Android 应用。