SparseArray分析

SparseArray是android里为<Interger,Object> 这样的Hashmap而专门写的类,目的是提高效率,其核心是折半查找函数(binarySearch)。

满足下面两个条件我们可以使用SparseArray代替HashMap:
1. 数据量不大,最好在千级以内
2. key必须为int类型或者long,这中情况下的HashMap可以用SparseArray或LongSparseArray代替

3、如果key类型为其它的类型,则使用ArrayMap


之后需要分析一下ArrayMap


参考:
https://liuzhichao.com/p/832.html

http://blog.csdn.net/u010687392/article/details/47809295 

public class SparseArray<E> implements Cloneable {
    private static final Object DELETED = new Object();
    private boolean mGarbage = false;   //是否可以回收,即清理mValues中标记为DELETED的值的元素

    private int[] mKeys;        //保存键的数组
    private Object[] mValues;   //保存值的数组
    private int mSize;          //当前已经保存的数据个数

    /**
     * Creates a new SparseArray containing no mappings.
     */
    public SparseArray() {
        this(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;
    }

    //通过二分查找法,在mKeys数组中查询key的位置,然后返回mValues数组中对应位置的值,找不到则返回默认值
    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];
        }
    }

    //通过二分查找法,在mKeys数组中查询key的位置,然后将mValues数组中对应位置的值设置为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;
            }
        }
    }

    //遍历mValues,将标记了DELETED的位置的右侧的值向左侧挪动,并重置mSize
    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) {
                if (i != o) {
                    keys[o] = keys[i];
                    values[o] = val;
                    values[i] = null;
                }
                o++;
            }
        }
        mGarbage = false;
        mSize = o;
    }

    //根据key的大小,计算它在mKeys中应该的位置,在该位置中插入key,和value
    public void put(int key, E value) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i >= 0) {   
            mValues[i] = value;
        } else {    //负值表示它应该插入到一个位置中,可能是已有的位置之间,也可能是最后
            i = ~i;

            //如果要插入到位置之间,但正好右侧的值为DELETED,就不需要插入了而是直接覆盖
            if (i < mSize && mValues[i] == DELETED) {
                mKeys[i] = key;
                mValues[i] = value;
                return;
            }

            //如果之前删除过某个value还没有进行垃圾回收,并且mKeys的值已经满了,即将需要扩容
            //则进行一次垃圾回收,缩小mKeys和mValues实际的元素数量mSize
            if (mGarbage && mSize >= mKeys.length) {
                gc();

                // Search again because indices may have changed.
                //一定还是找不到合适的位置而需要插入新对象
                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
            }

            //在位置i插入新元素
            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;
        }
    }

    //返回gc后真实的元素数量
    public int size() {
        if (mGarbage) {
            gc();
        }

        return mSize;
    }

    public int keyAt(int index) {
        if (mGarbage) {
            gc();
        }

        return mKeys[index];
    }

    public E valueAt(int index) {
        if (mGarbage) {
            gc();
        }

        return (E) mValues[index];
    }

    public void setValueAt(int index, E value) {
        if (mGarbage) {
            gc();
        }

        mValues[index] = value;
    }

    //通过二分查找,返回key所在的位置,可能是负值表示目前没有存储过key但是它应该的位置是多少
    public int indexOfKey(int key) {
        if (mGarbage) {
            gc();
        }

        return ContainerHelpers.binarySearch(mKeys, mSize, key);
    }

    //便利数组返回value所在位置,-1表示未找到
    public int indexOfValue(E value) {
        if (mGarbage) {
            gc();
        }

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

        return -1;
    }

    public void clear() {
        int n = mSize;
        Object[] values = mValues;
        for (int i = 0; i < n; i++) {
            values[i] = null;
        }
        mSize = 0;
        mGarbage = false;
    }

    public void append(int key, E value) {
        //key值在已有的key值数组的范围内,则调用put插入
        if (mSize != 0 && key <= mKeys[mSize - 1]) {
            put(key, value);
            return;
        }
        //key值在范围外,先gc,然后直接追加在数组最后
        if (mGarbage && mSize >= mKeys.length) {
            gc();
        }
        mKeys = GrowingArrayUtils.append(mKeys, mSize, key);
        mValues = GrowingArrayUtils.append(mValues, mSize, value);
        mSize++;
    }
}


public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
    assert currentSize <= array.length;

    //如果数组容量大于其中已经占用的元素数量,则将element插入到index位置,原来index以及之后的元素整体向后移动
    if (currentSize + 1 <= array.length) {
        System.arraycopy(array, index, array, index + 1, currentSize - index);
        array[index] = element;
        return array;
    }

    //此时currentSize和array.length相等,array扩容成newArray,复制并插入element
    T[] newArray = ArrayUtils.newUnpaddedArray((Class<T>)array.getClass().getComponentType(),
            growSize(currentSize));
    System.arraycopy(array, 0, newArray, 0, index);
    newArray[index] = element;
    System.arraycopy(array, index, newArray, index + 1, array.length - index);
    return newArray;
}

public static <T> T[] append(T[] array, int currentSize, T element) {
    assert currentSize <= array.length;

    //数组已满,需要扩容
    if (currentSize + 1 > array.length) {
        @SuppressWarnings("unchecked")
        T[] newArray = ArrayUtils.newUnpaddedArray(
                (Class<T>) array.getClass().getComponentType(), growSize(currentSize));
        System.arraycopy(array, 0, newArray, 0, currentSize);
        array = newArray;
    }
    //放在数组最后一个实际元素的后面
    array[currentSize] = element;
    return array;
}

//找到则返回所在的位置,否则返回它应该在的位置并取反
static int binarySearch(int[] array, int size, int value) {
    int lo = 0;
    int hi = size - 1;

    while (lo <= hi) {
        final int mid = (lo + hi) >>> 1;
        final int midVal = array[mid];

        if (midVal < value) {
            lo = mid + 1;
        } else if (midVal > value) {
            hi = mid - 1;
        } else {
            return mid;  // value found
        }
    }
    return ~lo;  // value not present
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值