SparseArray源码分析

本文详细解析了SparseArray的构造函数、put方法、get方法等核心功能实现原理,并介绍了其内部如何通过二分查找提高效率及如何处理删除操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码分析

首先来看构造函数:

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

这有两个构造函数,明显从第一个知道,默认的初始容量为10,接着第二个构造函数,那么我们来看EmptyArray:

//Google搜
public final class EmptyArray {
    private EmptyArray() {}
    public static final boolean[] BOOLEAN = new boolean[0];
    public static final byte[] BYTE = new byte[0];
    public static final char[] CHAR = new char[0];
    public static final double[] DOUBLE = new double[0];
    public static final int[] INT = new int[0];
    public static final Class<?>[] CLASS = new Class[0];
    public static final Object[] OBJECT = new Object[0];
    public static final String[] STRING = new String[0];
    public static final Throwable[] THROWABLE = new Throwable[0];
    public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];
}

接着ArrayUtils.newUnpaddedObjectArray用于创建优化后的数组,该方法实际上是一个Native方法,它解决了当数组中的元素没有填满时造成的空间浪费(知道这点就行吧,我也不清楚原因)
https://blog.youkuaiyun.com/easyer2012/article/details/37871031

接下来来看看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++;
        }
    }

先看看ContainerHelpers.binarySearch:

 // 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) {
            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
    }

一看到这个循环,就知道时采用的是二分法查找,这样间接的知道了,key这个数组也是按照大小排列好了的,如果没找到返回的是~lo,这个方法中,我们明确的知道lo一定会为回负数,返回的是~lo,也就是负数,这就标志了现在的key数组里面是没有put进来的key的,同时lo已经确定好了在key数组里面的位置,接下来有个判断mValues[i] == DELETED,这是怎么回事呢,按照前面的理解,返回的是负数,说明是数组里面没有,可以直接的加入put进来的就行了,多出来这个步骤有什么用,那主要的是DELETED这个标志,通过查找,我们可以发现,在delete相关的方法中出现了:

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

        if (i >= 0) {
            if (mValues[i] != DELETED) {
                mValues[i] = DELETED;
                mGarbage = true;
            }
        }
    }

只列出这一个,其他的也差不多意思,从这里可以看出来,删除的只是value,key是没有被删除的,这样就很好理解上面那个判断了
接着下面一个判断,通过全局查找,mGarbage意思就是执行过相关的delete方法,mGarbage会被赋值true,看看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++) {
            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;

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

这个方法大概意思就是把删掉的清除掉,重新排列当前的两个数组,把标记为DELETED除去,经过这gc操作,数组大小发生了改变,也就是put说要重新的找出应该放入的位置
接着就是把put的放入到数组当中GrowingArrayUtils.insert:

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

如果当前的数组还没有满,则在当前的数组里面增加,否则新建一个数组,把当前的数组里面的元素放入新数组,新数组长度为growSize(currentSize):

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

也就是增大两倍,最小为8
接着来看看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];
        }
    }    

非常容易懂把,那就不分析了,总的来说,分析完put,其他的都好理解了

2总结:

SparseArray是android里为

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值