SparseArray源码解析

10 篇文章 0 订阅

1.SparseArray简介  

    SparseArray的主要作用是将Integers映射到Objects,相当于Map<Integer,Object>。当需要将Integers映射到Objects时,SparseArray比HashMap更高效。因为SparseArray避免自动装箱keys,并且它的数据结构不依赖于外部的Entry。
    SparseArray是在一个数组结构中维护它的映射关系,通过二分查找来查找key。SparseArray不适合包含大量数据的场景,因为添加和删除一个元素时,需要先二分查找,找到key所对应的索引index,然后在数组中插入和删除一个value。如果需要包含大量的数据,建议选用HashMap。
    为了更好的性能,SparseArray在移除元素的时候,包含了一个优化:在删除元素的时候,不立即将该元素移除,而是标记该位置的元素被删除了。这样该key位置上的entry是可以被复用,当需要压缩数组的时候,将这些被标记为删除的entry全部被回收。数组压缩可以发生在任何时候,例如数组需要增大或是entry值需要回收时。
    可以通过keyAt(int)和ValueAt(int)来迭代它的选项。迭代keys可以通过keyAt(int)函数,迭代values可以通过valueAt(int)函数来获取。

2.SparseArray源码分析

//实现了Cloneable接口
public class SparseArray<E> implements Cloneable {
    private static final Object DELETED = new Object();  //DELETED对象,用来指示被删除的对象
    private boolean mGarbage = false;//指示是否垃圾回收
    private int[] mKeys;//保存key值的数组
    private Object[] mValues;//保存value值的数组
    private int mSize;//当前SparseArray的大小
//构造函数,默认元素数量为10个
public SparseArray() {
        this(10);
    }

 //带Capacity的构造函数,当Capacity为0时,不限定数组的大小
 public SparseArray(int initialCapacity) {
        if (initialCapacity == 0) {
            mKeys = EmptyArray.INT;
            mValues = EmptyArray.OBJECT;
        } else {
            mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity);
            mKeys = new int[mValues.length];
        }
        mSize = 0;
    }
}

//clone接口,实现key数组和value数组的拷贝
 public SparseArray<E> clone() {
        SparseArray<E> clone = null;
        try {
            clone = (SparseArray<E>) super.clone();
            clone.mKeys = mKeys.clone();
            clone.mValues = mValues.clone();
        } catch (CloneNotSupportedException cnse) {
            /* ignore */
        }
        return clone;
    }
//从指定的key值获取映射的Object
public E get(int key) {
        return get(key, null);
    }

public E get(int key, E valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//二分查找key值对应的索引值i
       //当索引值i小于0,或者值是被标记为删除时,返回valueIfKeyNotFound
    if (i < 0 || mValues[i] == DELETED) {
            return valueIfKeyNotFound;
        } else {
            return (E) mValues[i];
        }
    }

//将指定key对应的Value对象标记为DELETED删除
public void delete(int key) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

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

//与delete方法具有相同的功能
public void remove(int key) {
        delete(key);
}

//移除指定索引的Value对象
public void removeAt(int index) {
        if (mValues[index] != DELETED) {
            mValues[index] = DELETED;
            mGarbage = true;
        }
    }

//回收被标记DELETE的对象,将数组向前压缩
private void gc() {
        int n = mSize;
        int o = 0;
        int[] keys = mKeys;
        Object[] values = mValues;
        for (int i = 0; i < n; i++) {
            Object val = values[i];
             //如果val不等于DELETED对象,i和o都整体往后移动,否则只让i后移,这样DELETE对象会被后面的对象覆盖,DELETED对象后面的对象都整体向前移动
            if (val != DELETED) {
                if (i != o) {
                    keys[o] = keys[i];
                    values[o] = val;
                    values[i] = null;
                }
                o++;
            }
        }
        mGarbage = false;
        mSize = o;//更新数组压缩后的大小
    }
//将一个<key,value>对放入到数组中
public void put(int key, E value) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//二分查找key值对应的索引

        if (i >= 0) {
            //1.如果索引值大于0,说明数组已经存在该key了,直接更新key对应的value值
            mValues[i] = value;
        } else {
            i = ~i;//没有找到对应的索引,说明该<key,value>是新插入的,
            //2.如果索引i小于数组的大小,并且该索引i对应value是DELETED对象,说明该key对应的对象被移除过,现在可以复用该key,并且更新对应的value对象。
            if (i < mSize && mValues[i] == DELETED) {
                mKeys[i] = key;
                mValues[i] = value;
                return;
            }
            //如果存储空间不足,并且可以压缩数组空间,则先压缩数组空间,并且重新生成新的索引,因为压缩数组之后,数组元素的位置可能会发生变化,需要重新生成索引。
            if (mGarbage && mSize >= mKeys.length) {
                gc();
                // Search again because indices may have changed.
                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
            }
            //3.将key和value值分别插入key数组和value数组
            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;
        }
    }

// 返回数组的实际大小
 public int size() {
        if (mGarbage) {
            gc();
        }
        return mSize;
    }
 //返回指定索引i对于的key值
 public int keyAt(int index) {
        if (mGarbage) {
            gc();
        }
        return mKeys[index];
    }

//返回指定索引对于的value对象
 public E valueAt(int index) {
        if (mGarbage) {
            gc();
        }

        return (E) mValues[index];
    }
//在指定的索引i上,设置value值
 public void setValueAt(int index, E value) {
        if (mGarbage) {
            gc();
        }
        mValues[index] = value;
    }

//返回指定key值对应的索引i,通过二分查找返回对于的索引
public int indexOfKey(int key) {
        if (mGarbage) {
            gc();
        }
        return ContainerHelpers.binarySearch(mKeys, mSize, key);
    }

//返回指定value对象对应的索引i,通过线性查找
public int indexOfValue(E value) {
        if (mGarbage) {
            gc();
        }

        for (int i = 0; i < mSize; i++)
            if (mValues[i] == value)
                return i;

        return -1;
    }

//从SparseArray数组中清除key-value映射对
 public void clear() {
        int n = mSize;
        Object[] values = mValues;

        for (int i = 0; i < n; i++) {
            values[i] = null;
        }

        mSize = 0;
        mGarbage = false;
    }

//将一个<key,value>对添加到数组的末尾
public void append(int key, E value) {
        //1.key值小于最大的值,不能添加到数组的末尾,需要用put的方法将<key,value>存入
        if (mSize != 0 && key <= mKeys[mSize - 1]) {
            put(key, value);
            return;
        }

       //2.如果数组容量不够,需要先压缩数组,回收被删除的对象。
        if (mGarbage && mSize >= mKeys.length) {
            gc();
        }
        //3.将<key,value>添加到数组的末尾
        mKeys = GrowingArrayUtils.append(mKeys, mSize, key);
        mValues = GrowingArrayUtils.append(mValues, mSize, value);
        mSize++;
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值