Google为什么建议使用 SpareArray代替HashMap

         在做android开发很多年的时间里面,很多人都知道要使用 SpareArray,但是并不知道为什么。今天就来聊一聊SpareArray的实现源码,讲解下当前SpareArray的实现原理。

一、首先看下SpareArray的构造函数:

    public SparseArray() {
        this(10);
    }

    public SparseArray(int initialCapacity) {
        if (initialCapacity == 0) {
            mKeys = ContainerHelpers.EMPTY_INTS;
            mValues = ContainerHelpers.EMPTY_OBJECTS;
        } else {
            initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
            mKeys = new int[initialCapacity];
            mValues = new Object[initialCapacity];
        }
        mSize = 0;

    }

 默认是分配一个数组长度为10,分别分配一个保存int类型的key,一个保存Object类型的值。

二、增加相应的数据
它有两个方法可以添加键值对:
public void put(int key, E value)  
public void append(int key, E value)   

    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);
            }
            if (mSize >= mKeys.length) {
                int n = ArrayUtils.idealIntArraySize(mSize + 1);
                int[] nkeys = new int[n];
                Object[] nvalues = new Object[n];
                // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
                System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
                System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
                mKeys = nkeys;
                mValues = nvalues;
            }
            if (mSize - i != 0) {
                // Log.e("SparseArray", "move " + (mSize - i));
                System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
                System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
            }
            mKeys[i] = key;
            mValues[i] = value;
            mSize++;
        }

    }

     在上面添加数据的代码中发现,里面使用了一个非常重要的方法,就是二分查找法,来查找当前key的位置,然后,在适当的位置把当前值插入进去,因此,SparseArray添加的数组的内容一定是有序的。并且,它存储的数值都是按键值从小到大的顺序排列好的。其实append方法的实现方式和put的实现方式类似,这里不再详细讲解。

三、查
它有两个方法可以取值:
  1. public E get(int key)  
  2. public E get(int key, E valueIfKeyNotFound)  
最后一个从传参的变量名就能看出,传入的是找不到的时候返回的值
查看第几个位置的键:
public int keyAt(int index)  
查看第几个位置的值:
public E valueAt(int index)  
查看键所在位置,由于采用二分法查找键的位置,所以没有的话返回小于0的数值,而不是返回-1,这点要注意,返回的负数其实是表示它在哪个位置就找不到了,如果你存了5个,查找的键大于5个值的话,返回就是-6:     public int indexOfKey(int key)  
查看值所在位置,没有的话返回-1:
public int indexOfValue(E value)  

四、删
它有两个方法:
  1. public void delete(int key)  
  2. public void remove(int key)  
但其实,delete和remove的效果是一样的,remove方法中调用了delete方法,remove源码:public void remove(int key) {  
        delete(key);  
}  

还有一个clear方法,就是全部清除当前的所有数据。

通过上面的简单分析,基本可以知道,SparseArray使用的是纯数组的方式来实现的,不管是key,还是value都是一个数组。

而HashMap使用的是散列表(数组+链表)的方式实现的,查询数据的计算方式也比数组要复杂。关于HashMap的实现原理,将在下一个章节中讲解。另外,SparseArray实现了Cloneable接口,还可以调用clone方法。

对比当前的性能,总结如下:

1.SpareseArray 也是通过键值对存储数据,只是key为整形int , 类似于key = Interger 的HashMap,但是SpareseArray 的key 为 int 非 Interger ,更省空间。
2. SpareArray 意为稀松数组,其结构类似于数组结构,依次排开;HashMap是散列列表,根据hash值来存储;因此SpareArray 会比 HashMap节省很多空间。
3.从查找速度 和 插入效率来看,如果是正序插入( 0 ->size插入),SpareArray 的插入效率会高于 HashMap。
4.如果是逆序插入(size -> 0)的顺序插入,则SpareArray 的插入效率表现是最差的,会低于HashMap。
5. SpareArray 在逆序插入效率很低,是因为 每次插入 SpareArray 都会采用二分查找来定位。
6. 从查找速度来来考虑,HashMap的查找速度 会 高于 SparseArray。
于是,通过以上分析,SpareArray 相对于 HashMap的最大优势在内存空间。因此谷歌推荐使用 SpareArray 代替 HashMap 。

没有更多推荐了,返回首页