SparseArray基本使用及源码分析

一、简介

1、适用于键为int的map数据结构类型

2、适用于小规模数据存储

3、内部使用二分查找进行数据查询,查询效率高

4、不需要单独开辟内存来映射对象,节约内存

二、基本使用

1、创建mSparseArray全局变量

 val mSparseArray=SparseArray<String>()

2、添加操作

添加过程中通过添加两个键重复,值不重复的两个数据,发现键重复,值覆盖和hashMap相同

btn_sparse_array_put.setOnClickListener {
            mSparseArray.put(1,"Red")
            mSparseArray.put(2,"Green")
            mSparseArray.put(3,"Blue")
            mSparseArray.put(4,"Yellow")
            mSparseArray.put(4,"Gray")
            Log.e("peter", "size is-> ${mSparseArray.size()}  sparseArray is ->$mSparseArray")
        }

结果

03-19 03:47:44.396 29513-29513/com.gee.peter.testapplication E/peter: size is-> 4  sparseArray is ->{1=Red, 2=Green, 3=Blue, 4=Gray}

3、删除操作

删除操作,当删除的键不存在时,不执行删除操作,不会报错

 btn_sparse_array_delete.setOnClickListener {
            if (mSparseArray.size()==0)return@setOnClickListener

            mSparseArray.delete(0)// 根据键删除
            mSparseArray.delete(1)// 根据键删除
            mSparseArray.removeAt(2)// 删除索引为2的元素
            Log.e("peter","size is -> ${mSparseArray.size()}   sparseArray is->$mSparseArray")
        }

结果:
删除了键为1和索引为2的元素

size is -> 2   sparseArray is->{2=Green, 4=Gray}

4、修改操作

修改索引为0的元素值

 btn_sparse_array_modify.setOnClickListener {
            mSparseArray.setValueAt(0,"modifyColor")
            Log.e("peter","modifyColor is  -> ${mSparseArray.valueAt(0)}   sparseArray is->$mSparseArray")
        }

结果:

 modifyColor is  -> modifyColor   sparseArray is->{2=modifyColor, 4=Gray}

5、查询操作

通过添加和查询两个操作来实现

  btn_sparse_array_search.setOnClickListener {
            mSparseArray.put(1,"Red")
            mSparseArray.put(2,"Green")
            mSparseArray.put(3,"Blue")
            mSparseArray.put(4,"Yellow")
            mSparseArray.put(4,"Gray")
            for (i in 0..mSparseArray.size()){
                Log.e("peter","mSparseArray index -> $i  value is ->${mSparseArray.valueAt(i)}")
            }

结果:

mSparseArray index -> 0  value is ->Red 
mSparseArray index -> 1  value is ->Green
mSparseArray index -> 2  value is ->Blue
mSparseArray index -> 3  value is ->Gray
mSparseArray index -> 4  value is ->null

通过结果可见:虽然我们插入的键为4的数据已经在sparseArray中存在了,但是执行的操作是将键重复的进行值覆盖,但是仍然并未立即删除该索引,而是将其标记为删除状态,方便以后复用

三、主要方法源码解析
主要方法:

在这里插入图片描述
1、put方法

 /**
     * Adds a mapping from the specified key to the specified value,
     * replacing the previous mapping from the specified key if there
     * was one.
     */
    public void put(int key, E value) {
        // 利用二分查找查询是否有该键
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i >= 0) {
            mValues[i] = value; // 存在该键直接获取该元素修改对应的值
        } else {
            i = ~i;          // 否则i<0,执行非操作

            if (i < mSize && mValues[i] == DELETED) {  
            // 如果已被标记为删除状态了并且i<size,表明array的容量足够,并且之前已经申请了存储空间,直接进行赋值即可
                mKeys[i] = key;                      
                mValues[i] = value;
                return;
            }
            // 如果需要执行垃圾回收,并且array的容量大于mKeys的长度,也就是说array申请了多余的空间
            if (mGarbage && mSize >= mKeys.length) {
                gc();   // 执行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++;    // mSize增加
        }
    }

2、删除数据

remove(int key)通过键删除数据

   /**
     * Alias for {@link #delete(int)}.
     */
    public void remove(int key) {
        delete(key);
    }
    
     /**
     * Removes the mapping from the specified key, if there was any.
     */
    public void delete(int key) {
    // 查询该key在mKsys中对应的索引
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        // 标记为DELETED,将垃圾回收标记改为true
        if (i >= 0) {
            if (mValues[i] != DELETED) {
                mValues[i] = DELETED;
                mGarbage = true;
            }
        }
    }

removeAt(int index) 通过索引删除

 public void removeAt(int index) {
        // 标记为DELETED,将垃圾回收标记修改为true
        if (mValues[index] != DELETED) {
            mValues[index] = DELETED;
            mGarbage = true;
        }
    }

removeAtRange(int index, int size) 删除从指定开始的几个元素

   /**
     * Remove a range of mappings as a batch.
     *
     * @param index Index to begin at
     * @param size Number of mappings to remove
     *
     * <p>For indices outside of the range <code>0...size()-1</code>,
     * the behavior is undefined.</p>
     */
    public void removeAtRange(int index, int size) {
    // 防止索引越界,取mSize与index+size的最小值,机智
        final int end = Math.min(mSize, index + size);
        for (int i = index; i < end; i++) {
            removeAt(i);
        }
    }

3、修改数据 setValueAt(int index, E value)

 /**
     * Given an index in the range <code>0...size()-1</code>, sets a new
     * value for the <code>index</code>th key-value mapping that this
     * SparseArray stores.
     *
     * <p>For indices outside of the range <code>0...size()-1</code>, the behavior is undefined.</p>
     */
    public void setValueAt(int index, E value) {
    // 先进行垃圾回收,及时释放内存
        if (mGarbage) {
            gc();
        }
        // 赋值    
        mValues[index] = value;
    }

4、查询操作

get(int key) 通过键查询值 默认值为null

    /**
     * Gets the Object mapped from the specified key, or <code>null</code>
     * if no such mapping has been made.
     */
    public E get(int key) {
        return get(key, null);
    }
    
      /**
     * Gets the Object mapped from the specified key, or the specified Object
     * if no such mapping has been made.
     */
    @SuppressWarnings("unchecked")
    public E get(int key, E valueIfKeyNotFound) {
        // 通过二分查找查询索引
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        
        if (i < 0 || mValues[i] == DELETED) {
        // 未查询到返回未查询到的值,默认值为null
            return valueIfKeyNotFound;
        } else {
        // 返回该键对应的值
            return (E) mValues[i];
        }
    }

keyAt(int index) 通过索引获取键值

  public int keyAt(int index) {
        if (mGarbage) {
            gc();
        }
    // 获取keys中index对应的key值
        return mKeys[index];
    }

valueAt(int index) 通过索引获取值

  @SuppressWarnings("unchecked")
    public E valueAt(int index) {
        if (mGarbage) {
            gc();
        }
        // 获取该index在mValues中对应的value值
        return (E) mValues[index];
    }

indexOfKey(int key) 通过key查询对应的索引

 public int indexOfKey(int key) {
        if (mGarbage) {
            gc();
        }
    // 使用二分查找法,通过key查询对应索引
        return ContainerHelpers.binarySearch(mKeys, mSize, key);
    }

indexOfValue(E value) 通过索引查询对应的value值

   public int indexOfValue(E value) {
        if (mGarbage) {
            gc();
        }
        // 通过遍历进行查询索引
        for (int i = 0; i < mSize; i++) {
        // 可以用来比较基本数据类型和引用数据类型
            if (mValues[i] == value) {
                return i;
            }
        }
        // 未查询到返回-1
        return -1;
    }

indexOfValueByValue(E value) 通过equals方法判断获取index

  public int indexOfValueByValue(E value) {
        if (mGarbage) {
            gc();
        }

        for (int i = 0; i < mSize; i++) {
            if (value == null) {
                if (mValues[i] == null) {
                    return i;
                }
            } else {
            // 使用equals比较如果对应的类未重写equals方法,则用于比较引用数据类型的地址值
                if (value.equals(mValues[i])) {
                    return i;
                }
            }
        }
        return -1;
    }

5、gc() 垃圾回收方法

  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++) {
            // 获取values对应的数据
            Object val = values[i];

            if (val != DELETED) {
                if (i != o) {   // 只有当i!=o时触发
                    // 将value向前移动一位,将values[i]置为null
                    keys[o] = keys[i];   // 键移动 将索引 i 处的 key 赋值给 o 处的key
                    values[o] = val;     // 值移动 将索引 i 处的 value 赋值给 o 处的value
                    values[i] = null;    
                }

                o++;
            }
        }

        mGarbage = false;
        mSize = o;

        // Log.e("SparseArray", "gc end with " + mSize);
    }

**6、使用的最多的二分查找 binarySearch(int[] array, int size, int value) **

  // This is Arrays.binarySearch(), but doesn't do any argument validation.
    static int binarySearch(int[] array, int size, int value) {
        int lo = 0;
        int hi = size - 1;

        while (lo <= hi) {
            // 通过位右移获取mid
            final int mid = (lo + hi) >>> 1;
            // 通过获取获取mid对应的索引值
            final int midVal = array[mid];
            
            if (midVal < value) {
                lo = mid + 1;   // 改变lo的index
            } else if (midVal > value) {
                hi = mid - 1;   // 改变hi的index
            } else {
                return mid;  // value found
            }
        }
        return ~lo;  // value not present
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值