SparseArray的使用

SparseArray简介

SparseArray是android.util包中一个用于映射int和对象的数据结构,内部使用数组实现,分别将key和value保存在两个数组中,它避免了自动装箱操作,同时其数据结构不依赖于每个映射对象,因此比起HashMap来说更加高效。
特点:

  • 1.通过二分法查找数据。
  • 2.不适合存储大数据。
  • 3.比起HashMap,执行效率慢,查找通过二分法,添加和删除需要插入和删除数组中的条目。
    为了提高性能,SparseArray在删除时做了一个优化,不会实际删除项,而是将需要删除的对象做一个标记。

API:

构造方法:

SparseArray():默认容量为10.
SparseArray(int initialCapacity):指定容量为initialCapacity

取值方法:

E get(int key):获取对应Key的值。
public E get(int key, E valueIfKeyNotFound):获取对应Key的值,没有则返回给定valueIfKeyNotFound。
E valueAt(int index):获取指定索引的value值

取key方法:

int keyAt(int index):返回指定索引的key值

获取索引方法:

int indexOfKey(int key):获取指定key的索引值
int indexOfValue(E value):获取指定value的索引值,对value使用”==”进行比较
int indexOfValueByValue(E value):获取指定value的索引值,对value使用equals()进行比较

删除方法:

void delete(int key):删除指定key的值,如果不存在key则不进行任何操作。
void remove(int key):内部实现调用delete().
void removeAt(int index):删除指定索引位置的值。
void removeAtRange(int index, int size)。删除index-index+size区间的值。
E removeReturnOld(int key):删除并返回指定key的值。
void clear():清空SparseArray。

添加一对映射方法:

void put(int key, E value):添加指定key和value,如果已经存在key,则将该key的value替换为本次传入的value。
void append(int key, E value):添加指定key和value,并且针对键大于数组中所有现有键的情况进行优化。

修改值方法:

void setValueAt(int index, E value):替换指定索引位置的value。

其他方法:

int size():获取SparseArray的中存储的个数。
SparseArray clone():克隆SparseArray,实现了Cloneable接口。
String toString():以{key=obj}的形式返回一个字符串。

内部实现:

SparesArray内部采用了两个数据来分别存储key和value,如下:

public class SparseArray<E> implements Cloneable {
    private static final Object DELETED = new Object();
    private boolean mGarbage = false;

    private int[] mKeys;
    private Object[] mValues;
    private int mSize;

在删除映射对时,并不是将对应值删除,而是保留key,给value添加一个标记:DELETED,从而可以重用该key,最后会在垃圾回收步骤中进行删除,如:

    public void delete(int key) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i >= 0) {
            if (mValues[i] != DELETED) {
                mValues[i] = DELETED;
                mGarbage = true;
            }
        }
    }

垃圾回收方法如下:


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];

        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);
}
SparseArray 是 Android 提供的一种专为映射 `int` 到 `Object` 而设计的数据结构,其内部通过两个独立的数组分别存储键(`int` 类型)和值(`Object` 类型),并且通过二分查找算法来提高查找效率[^4]。这种实现方式避免了 `HashMap` 中由于自动装箱导致的额外开销,并且在特定场景下比 `HashMap` 更加高效。 ### 数据结构 SparseArray 使用两个数组分别存储键和值: - `mKeys[]`:用于存储键的 `int` 数组。 - `mValues[]`:用于存储值的 `Object` 数组。 这两个数组的长度始终保持一致,且每个键值对在两个数组中的索引位置相同。SparseArray 内部采用二分查找算法进行键的查找,使得查找的时间复杂度为 `O(log n)`,而插入和删除操作则需要移动数组元素,时间复杂度为 `O(n)`[^4]。 当初始化 SparseArray 时,可以通过指定初始容量,否则默认容量为 10。实际分配的数组大小会通过 `ArrayUtils.idealIntArraySize()` 方法进行优化,以适应内存对齐策略[^3]。 ### 添加与删除操作 SparseArray 提供了两种添加元素的方式: - `put(int key, E value)`:根据键插入或更新值。 - `append(int key, E value)`:直接在数组末尾追加键值对,适用于键值递增的场景,效率高于 `put` 方法。 删除操作包括: - `delete(int key)`:标记删除指定键的条目。 - `remove(int key)`:功能与 `delete` 相同,但内部实现上略有不同。 值得注意的是,SparseArray 在删除元素时并不会立即压缩数组,而是将对应位置标记为空,后续插入操作可能会复用这些空位,从而减少数组扩容的频率[^2]。 ### 遍历与查找 SparseArray 提供了以下方式遍历数据: - 通过 `size()` 方法获取当前元素数量,结合 `keyAt(int index)` 和 `valueAt(int index)` 进行遍历。 - 可以直接使用 `get(int key)` 获取指定键的值。 查找操作通过二分法实现,时间效率为 `O(log n)`,适用于数据量较小的场景。 ### 使用场景 SparseArray 主要适用于以下场景: - 数据量较小(通常在几百以内),稀疏分布的键值映射。 - 键为 `int` 类型,避免使用 `Integer` 装箱带来的性能开销。 - 对查找效率有一定要求,但插入和删除操作相对较少。 - 在 Android 系统中,SparseArray 被广泛用于 UI 组件、事件映射等场景,例如 `RecyclerView` 中的 ViewHolder 缓存管理、事件监听器的注册等。 ### 示例代码 ```java SparseArray<String> sparseArray = new SparseArray<>(); // 添加元素 sparseArray.put(1, "Value1"); sparseArray.append(2, "Value2"); // 修改元素 sparseArray.put(1, "NewValue"); // 删除元素 sparseArray.delete(2); // 遍历元素 for (int i = 0; i < sparseArray.size(); i++) { int key = sparseArray.keyAt(i); String value = sparseArray.get(key); Log.d("SparseArrayExample", "Key: " + key + ", Value: " + value); } ``` ### 优缺点分析 **优点:** - 避免了 `HashMap` 中 `Integer` 装箱的开销。 - 使用二分查找提升查找效率。 - 对内存使用更友好,尤其在数据量较小时。 **缺点:** - 插入和删除操作效率较低,尤其是数据量较大时。 - 不适合处理大规模数据集。 - 不支持并发访问,需手动加锁。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值