SparseArray和ArrayMap的内部实现

1、SparseArray

SparseArrays map integers to Objects.  Unlike a normal array of Objects,there can be gaps in the indices.  It is intended to be more memory efficient than using a HashMap to map Integers to Objects, both because it avoids auto-boxing keys and its data structure doesn't rely on an extra entry object for each mapping.

上面这段话来自SparseArray类的声明,重点关注一下: 与使用HashMap将Integers映射到Objects相比,它的内存效率更高,因为它避免了自动装箱键,并且其数据结构不依赖于每个映射的额外条目对象。(这也是SparseArray的优点

SparseArray包含三个主要成员变量:

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

其中,mKeys用于存放键key,mValues用来存放值(泛型E)。mSize为键值对的数量。

初始容量默认为10。

先看put方法:

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

        if (i >= 0) {
            mValues[i] = value;
        } else {
            i = ~i;

            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);
            }

            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;
        }
    }

put方法首先在mKeys数组中通过二分查找算法,找到key在mKeys数组中的索引 i,如果i>0,即已经存在一个键值对,则直接覆盖mValues数组中的值。如果i<0,即不存在该键值对,但是由于mKeys数组是从小到大排好序的,-i为key需要插入mKeys数组中的位置,并且mkeys和mValues数组还有剩余空间并且-i索引位置的值为空,表示不需要扩容并且不需要数据迁移,则把key存放在mKeys数组的-i索引位置,value存放在mValues数组-i索引位置。

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

        if (currentSize + 1 <= array.length) {
            System.arraycopy(array, index, array, index + 1, currentSize - index);
            array[index] = element;
            return array;
        }

        long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize));
        System.arraycopy(array, 0, newArray, 0, index);
        newArray[index] = element;
        System.arraycopy(array, index, newArray, index + 1, array.length - index);
        return newArray;
    }

如果,当前mKeys数组和mValues数组已满,或者-i索引位置已经有值。则通过GrowingArrayUtils.insert方法把key插入到-i索引位置,并将-i(包含)以后的key向后平移一个单位来完成key迁移。如果mKeys数组需要扩容,则调用GrowingArrayUtils.growSize方法进行扩容

 public static int growSize(int currentSize) {
        return currentSize <= 4 ? 8 : currentSize * 2;
    }

扩容规则为:当前容量不大于4时直接扩容到8,否则双倍扩容,由于前面说过初始容量为10,所以都是双倍扩容。扩容完成后,在完成上面的key迁移过程。

再看get方法

 public E get(int key) {
     return get(key, null);
 }
  
 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数组中找到对应的索引,如果索引存在则直接返回mValues数组中该索引下的值。否则返回null。

2、ArrayMap

    int[] mHashes;
    Object[] mArray;
    int mSize;
ArrayMap同样也是三个重要成员变量:1,mHashes用于存放key对象的hashcode值,该值也按照从小到大的顺序排序。2,mArray用于存放key和value对象。具提存放策略稍后讲。3,mSize为key-value键值对的数量。

同样先看put方法

@Override
    public V put(K key, V value) {
        final int osize = mSize;
        final int hash;
        int index;
        if (key == null) {
            hash = 0;
            index = indexOfNull();
        } else {
            hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
            index = indexOf(key, hash);
        }
        if (index >= 0) {
            index = (index<<1) + 1;
            final V old = (V)mArray[index];
            mArray[index] = value;
            return old;
        }

        index = ~index;
        if (osize >= mHashes.length) {
            final int n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1))
                    : (osize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);

            if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);

            final int[] ohashes = mHashes;
            final Object[] oarray = mArray;
            allocArrays(n);

            if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
                throw new ConcurrentModificationException();
            }

            if (mHashes.length > 0) {
                if (DEBUG) Log.d(TAG, "put: copy 0-" + osize + " to 0");
                System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
                System.arraycopy(oarray, 0, mArray, 0, oarray.length);
            }

            freeArrays(ohashes, oarray, osize);
        }

        if (index < osize) {
            if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (osize-index)
                    + " to " + (index+1));
            System.arraycopy(mHashes, index, mHashes, index + 1, osize - index);
            System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
        }

        if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
            if (osize != mSize || index >= mHashes.length) {
                throw new ConcurrentModificationException();
            }
        }
        mHashes[index] = hash;
        mArray[index<<1] = key;
        mArray[(index<<1)+1] = value;
        mSize++;
        return null;
    }

从上面可以看出,put方法先判断key是否为空,如果为空,则hashcode为0,否则获取key的hashcode值,然后根据hashcode值在mKeys数组中找到对应的索引index,如果index>=0,则在mArray数组中2*index索引位置存放key对象,2*index+1索引位置存放value对象。如果index<0,表示没找到则-index为当前hashcode要插入到mKeys数组中的位置。如果mHashes数组需要扩容,则判断当前mSize是否大于等于8(BASE_SIZE*2),如果是则扩容为原来的1.5倍,否则判断mSize是否大于等于4,如果是则扩容为8,否则扩容为4。扩容完成后,将数据迁移到新的数组中。

get方法与SparseArray差不多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值