Android-SparseArray解读

1、SparseArray是什么

对于Android这样的移动设备来讲,内存的大小至关重要,然而HashMap虽然很强大,但是HashMap中采用了好几种数据结构,复杂度很高,同时浪费了空间,SparseArray就应运而生了。

SparseArray翻译过来就是稀疏数组,采用两个一维数组存储key和value,其中key仅支持整形元素,这也直接省略了自动装箱的步骤。

在于HashMap直接计算出索引不同,SparseArray是通过二分法搜索key的,因此在搜索性能上SparseArray不如HashMap高效。

2、SparseArray核心代码注释


    //专门用来表示闲置位置的
    private static final Object DELETED = new Object();
    private boolean mGarbage = false;//表示是不是需要进行GC

    @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
    private int[] mKeys;//存储key的数组,key只能为int
    @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, E)
    private Object[] mValues;//存储value的数组,可以为任意类型
    @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
    private int mSize;//实际使用的大小。我们声明的数组大小并不是完全使用的
    /**
     *构造函数
     */
    public SparseArray() {
    
    
        this(10);
    }
    //初始化数组长度为10
    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;
    }

    //将数据添加到两个数组对应位置
    public void put(int key, E value) {
    
    
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//通过二分法搜索
        if (i >= 0) {
    
    //i>=0说明搜到了,直接给对应位置赋值即可
            mValues[i] = value;
        } else {
    
    
            //否则的话计算出对应位置,这里的二分法搜的位置有两个,一个是0,一个是当前元素数量位置,也就是有效的后一个位置
            //打个比方,搜索数组[1,3,4,5,6,无效,无效,无效...],如果搜一个大于6的数,那么二分法那里返回的是第一个无效位置索引的按位取反
            //也就是~5,在下面这句话中就是~~5也就是5,如果搜一个小于1的数,返回的就是~0,在下面就是~~0.表示插入位置
            i = ~i;
            if (i < mSize && mValues[i] == DELETED) {
    
    //检查对应位是不是没有使用。如果没有,就直接赋值
                mKeys[i] = key;
                mValues[i] = value;
                return;
            }
            if (mGarbage && mSize >= mKeys.length) {
    
    //如果没有进行GC(这里的GC是指数组的压缩)就压缩一下
                gc();
                // Search again because indices may have changed.
                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);//然后搜到位置
            }
            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);//底下就是ArrayCopy的操作了
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;//有用的数据集大小加1
        }
    }
    //get方法
    public E get(int key) {
    
    
        return get(key, null);
    }
    //get方法很好理解,直接使用二分法搜索key数组中key的位置,然后key的位置也对应着value的位置
    //时间复杂度log(n)。还要检查一下搜没搜到。
    public E get(int key, E valueIfKeyNotFound) {
    
    
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i < 0 || mValues[i] == DELETED) {
    
    
            return valueIfKeyNotFound;
        } else {
    
    
            return (E) mValues[i];
        }
    }

    //删除和查找的方式是一样的,只不过删除过程是对数组位置赋值为DELETE的过程
    public void delete(int key) {
    
    
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        if (i >= 0) {
    
    
            if (mValues[i] != DELETED) {
    
    
                mValues[i] = DELETED;
                mGarbage = true;
            }
        }
    }

    //此函数的作用是把数组中有效元素提到数组前面,然后把原位置置为null,这样原位置的元素就可以被GC了
    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];
            if (val != DELETED) {
    
    
                //把key数组和value数组的有效元素移动到数组的前面,再把原位置置为null
                if (i != o) {
    
    
                    keys[o] = keys[i];
                    values[o] = val;
                    values[i] = null;//置为null,下一次GC就可以收集了
                }
                o++;
            }
        }

        mGarbage = false;//移动完成
        mSize = o;//新的数组大小
    }

猜你喜欢

转载自blog.csdn.net/qq_23594799/article/details/105413313
今日推荐