美文网首页
SparseArray源码

SparseArray源码

作者: 陆元伟 | 来源:发表于2020-05-30 17:59 被阅读0次

首先查看put方法,它限定只能由int类型的作为key

 public void put(int key, E value) {
        //1 二分查找key对应的index,如果没有查到,返回应该对应位置的~位,
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        //找到,直接更新值
        if (i >= 0) {
            mValues[i] = value;
        } else {
            
            i = ~i;
            //2 如果该位置已经有个,则直接赋值,
            if (i < mSize && mValues[i] == DELETED) {
                mKeys[i] = key;
                mValues[i] = value;
                return;
            }
            //3如果做了删除操作
            if (mGarbage && mSize >= mKeys.length) {
                //把删除的地方置空
                gc();
                //删除后数据长度有变化,要重新获取它的索引值
                // Search again because indices may have changed.
                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
            }
            //在i的位置插入key
            mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
            //在i的位置插入value
            mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
            mSize++;
        }
    }

注释1 为什么可以对key,二分查找? 理解binarySearch后的代码后才恍如大悟
binarySearch方法,乍一看,以为只是个二分查找,但是看到它返回了位置的非~ 操作后,细细品代码,发现通过这个返回值在做一个非操作,就可以对数组排好序了。举个栗子,假设现在key数组里面是[1,2,4,6],要插入的key是3,则要插入索引值为2,则该方法返回了 ~2,
如果找到该值,则返回对应的索引,如果没有找到,则返回应该要插入的索引值的~ 位,这样外面就可以根据返回值判断是否找到未找到,并且再通过一个非~操作,就可以拿到它应该要插入的位置,这样就把数据排好序了

binarySearch方法如下,一遍没看明白,多看几遍,就能发现它的不一般

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
    }

要明白注释2 就得看它的删除方法,它删除后,数组长度还是不变,只是删除的那个位置对象指向了一个DELETED对象。这样如果delete和put都是同一个key的话,只需要对那个索引值的位置赋值即可,不需要任何复制数据的操作。

 public void delete(int key) {
        //查找,和上面方法一样
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        //找到位置
        if (i >= 0) {
            //不是直接置空,而是指向一个DELETED对象
            if (mValues[i] != DELETED) {
                mValues[i] = DELETED;
                mGarbage = true;
            }
        }
    }

删除后它的长度都不变,那这样子获取它的长度岂不是有问题?继续看它获取size的方法,mGarbage变量是在删除时才会设置为true,这个时候我们就要看gc方法了

public int size() {
    if (mGarbage) {
        gc();
    }

    return mSize;
}

在这个方法里面,可以看到它把数组里面的DELETE的位置的都去掉了。重新修正了mSize值。

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

在GrowingArrayUtils方法里面看到,他增长长度的方式,如果小于要么是8,或者当前长度的2倍

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

get方法,通过二分查找方式找到对应的索引,如果没有找到,或者那个位置已经delete了,返回null

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

总结下:

key(不是它的hashcode)保存在mKeys[]的下一个可用的位置上。所以不会再对key自动装箱了。
value保存在mValues[]的下一个位置上,value还是要自动装箱的,如果它是基本类型。
查找的时候:
查找key还是用的二分法查找。也就是说它的时间复杂度还是O(logN)
知道了key的index,也就可以用key的index来从mValues中检索出value。
相较于HashMap,我们舍弃了Entry和Object类型的key,放弃了HashCode并依赖于二分法查找。在添加和删除操作的时候有更好的性能开销

相关文章

网友评论

      本文标题:SparseArray源码

      本文链接:https://www.haomeiwen.com/subject/oushzhtx.html