SparseArray 源码解析及hashMap性能对比

SparseArray数据结构:

private int[] mKeys;  // int 数组 保存 key
private Object[] mValues;  // Object 数组 存value
private int mSize;  // SparseArray 的长度 
SparseArray sparseArray = new SparseArray<>();// 还有同胞兄弟 SparseBooleanArray,LongSparseArray
 sparseArray .put(1,1);
 public void put(int key, E value) {
        // 由于采用二分法查找键的位置,所以没有的话返回小于0的数值,而不是返回-1,
        // 这点要注意,返回的负数其实是表示它在哪个位置就找不到了,
        // 如果你存了5个,查找的键大于5个值的话,返回就是-6:
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i >= 0) {
            // 查找到,修改数组的值
            mValues[i] = value;
        } else {
            //没找到 对-1 再取反 i= 0;
            i = ~i;

            // DELETED 删除标志 相当于 HashMap 赋值 == null
            // mSize SparseArray的长度
            // i 索引值 小于 数组长度 且对应的值需要被 移除
            // delete方法中并没有直接把元素移除掉,而是将值改为DELETE这个固定值 ,并且将flag置为true;
            if (i < mSize && mValues[i] == DELETED) {
                mKeys[i] = key;
                mValues[i] = value;
                return;
            }

            if (mGarbage && mSize >= mKeys.length) {
                gc();// 调用gc 重排序数组

                // Search again because indices may have changed.
                // 再次搜索,因为索引可能已经改变。gc中会做些改变
                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
            }

            //将key 和value 将插入数组
            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;
        }
    }
  • 总体过程 就是先二分查找 keys数组中是否有这个值,有直接覆盖掉values[],其余不变,查找不到将key 和 value 插入到数组中
    // 二分查找 基础不浪费时间
    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
    }
    private void gc() {
        // Log.e("SparseArray", "gc start with " + mSize);

        int n = mSize;
        int o = 0;
        int[] keys = mKeys;
        Object[] values = mValues;

        for (int i = 0; i < n; i++) {
            Object val = values[i];

            // 1 2 3 X 5 6,当出现要移除的数时 X,i > o 循环结束后 1 2 3 4 5 null
            // 和HashMap 一样,移除 制null
            if (val != DELETED) {
                if (i != o) {
                    keys[o] = keys[i];
                    values[o] = val;
                    values[i] = null;
                }

                o++;
            }
        }
        mGarbage = false;
        mSize = o;// 重新更新数组的长度,
  }

gc方法就是把 移除的数据 key和value都被数组中后一个 正常的数据 key 和value覆盖,最后移除的数据被移动到数组最后,并被置 null。

    // GrowingArrayUtils.insert 可以参考ArrayList 扩容
   public static <T> T[] insert(T[] array, int currentSize, int index, T element) {  
       assert currentSize <= array.length;  
       if (currentSize + 1 <= array.length) {  
           System.arraycopy(array, index, array, index + 1, currentSize - index);  
           array[index] = element;  
           return array;  
       }  
       @SuppressWarnings("unchecked")  
       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;  
   }
  1. SparseArray应用场景:

    • 数据量不大,最好在千级以内
    • key 必须 为int类型,这中情况下可以用SparseArray代替HashMap

我有看到好多帖子 .插入性能时间对比 倒序插入效率很差但是都没有说明为什么差,难受
2. SparseArray 较HashMap 性能优化

  • SparseArray只能存储 key 是int 类型,并且不需要自动装箱 Integer;HashMap 全能,需要自动装箱成包装类
  • SparseArray 底层是两个数组一个存key 一个存value 而不是HashMap中的稀疏存储;HashMap 底层时 数组加链表(JDK1.8 红黑树)。什么是稀疏数组(Sparse array)?
  • 查找时 SparseArray 二分查找 时间复杂度O()=O(logn);HashMap 红黑树的时间复杂度O(N)

ArrayMap每存储一条信息,需要保存一个hash值,一个key值,一个value值,当出现哈希冲突时,会在index的相邻位置插入;也是二分查找,两个数组

最後 看到网上有SparseArray 正反排序不同的说法,附上我得测试 百思不得其解

        @Override
        public void onClick(View v) {
            long start = System.currentTimeMillis();
            int MAX = 100000;
            Map<Integer, String> hash = new HashMap<Integer, String>();
//            SparseArray sparseArray = new SparseArray();

//            for (int i = MAX; i > 0; i--) {
//                hash.put(MAX, i + "");
//            }

//            for (int i = MAX; i > 0; i--) {
//                sparseArray.put(MAX, i + "");
//            }

//            for (int i = 0; i < MAX; i++) {
//                sparseArray.put(i, i + "");
//            }

            for (int i = 0; i < MAX; i++) {
                hash.put(i, i + "");
            }

            long ts = System.currentTimeMillis() - start;
            System.out.println("正序插入====" + ts);

结果

05-29 15:57:40 Map倒序插入====1706
05-29 15:57:50 倒序插入====105
05-29 15:57:51倒序插入====111
05-29 15:57:53倒序插入====100



05-29 15:59:34array倒序插入====1505
05-29 15:59:36倒序插入====96
05-29 15:59:37倒序插入====87
05-29 15:59:38倒序插入====91
05-29 15:59:39倒序插入====99

-----------------------------------------------------------------------------------------------------

05-29 16:05:39Map正序插入====1843
05-29 16:05:40正序插入====215
05-29 16:05:41正序插入====193
05-29 16:05:42正序插入====200
05-29 16:05:42正序插入====209
05-29 16:05:43正序插入====192




05-29 16:04:16 array正序插入====1680
05-29 16:04:18 正序插入====195
05-29 16:04:20 正序插入====195
05-29 16:04:21 正序插入====203
05-29 16:04:22 正序插入====204
05-29 16:04:23 正序插入====198

很清楚,讲道理每次我点击都是new对象的,怎么时间不同呢

猜你喜欢

转载自blog.csdn.net/guojiayuan002/article/details/80494091
今日推荐