SparseArray和ArrayMap相关源码解析

来源:互联网 发布:付磊 武汉大学 知乎 编辑:程序博客网 时间:2024/06/08 12:09

内存优化:Android API当中提供了一些优化过后的数据集合工具类,如SparseArray,SparseBooleanArray,以及LongSparseArray等,使用这些API可以让我们的程序更加高效。本文整理SparseArray和ArrayMap相关内部实现。

一、SparseArray

SparseArray的特点避免了key为int值时的自动装箱,优化了性能。

1、构造函数

  public SparseArray() {        this(10);    }
预先设置的容器大小为10。

2、变量

    private int[] mKeys;    private Object[] mValues;
两个数组进行存储,一个存储key,一个存储value。
3、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++;        }    }
put方法中采用了二分查找比较元素key值的大小,并进行排序。
#ContainerHelpers  二分查找

 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    }
4、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];        }    }
查找元素的方法内部也进行了二分查找。由于存储的时候都是按顺序进行排序的,所以取出元素的时候效果比较高。
5、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;            }        }    }
删除元素同样使用了二分查找。

总结:增加元素、删除元素、查找元素都使用了二分查找。所以SparseArray适用于数据量不大,并且key为int值的时候。
同样的,SparseBooleanArray适用于key为boolean的情况,内部实现相似。

二、ArrayMap

public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V>
ArrayMap继承SimpleArrayMap,实现了Map接口。以key-value的形式存储数据,相比HashMap会占用更少的内存空间。

1、变量

    int[] mHashes;    Object[] mArray;
mHashes存储了key的hashcode,mArray存储了key和value的值。
2、put方法

    public V put(K key, V value) {        final int hash;        int index;        if (key == null) {            hash = 0;            index = indexOfNull();        } else {            hash = key.hashCode();            index = indexOf(key, hash);        }        if (index >= 0) {            index = (index<<1) + 1;            final V old = (V)mArray[index];            mArray[index] = value;            return old;        }        index = ~index;        if (mSize >= mHashes.length) {            final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))                    : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);            if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);            final int[] ohashes = mHashes;            final Object[] oarray = mArray;            allocArrays(n);            if (mHashes.length > 0) {                if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");                System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);                System.arraycopy(oarray, 0, mArray, 0, oarray.length);            }            freeArrays(ohashes, oarray, mSize);        }        if (index < mSize) {            if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)                    + " to " + (index+1));            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);            System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);        }        mHashes[index] = hash;        mArray[index<<1] = key;        mArray[(index<<1)+1] = value;        mSize++;        return null;    }
需要保证mHashes是有序数组,才可以进行二分查找。

如果新增加的数据的key对应的hashcode值在中间的位置,需要将这个索引之后的全部数据向后移动。

如果元素的个数大于数组的长度,需要进行扩容。

3、get方法

public V get(Object key) {        final int index = indexOfKey(key);        return index >= 0 ? (V)mArray[(index<<1)+1] : null;}
indexOfKey方法,是通过key对应的hashcode获取到其在mHashes数组中的索引值。内部最终调用了indexOf方法。

   int indexOf(Object key, int hash) {        final int N = mSize;        // Important fast case: if nothing is in here, nothing to look for.        if (N == 0) {            return ~0;        }        int index = ContainerHelpers.binarySearch(mHashes, N, hash);        // If the hash code wasn't found, then we have no entry for this key.        if (index < 0) {            return index;        }        // If the key at the returned index matches, that's what we want.        if (key.equals(mArray[index<<1])) {            return index;        }        // Search for a matching key after the index.        int end;        for (end = index + 1; end < N && mHashes[end] == hash; end++) {            if (key.equals(mArray[end << 1])) return end;        }        // Search for a matching key before the index.        for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {            if (key.equals(mArray[i << 1])) return i;        }        // Key not found -- return negative value indicating where a        // new entry for this key should go.  We use the end of the        // hash chain to reduce the number of array entries that will        // need to be copied when inserting.        return ~end;    }

(1)通过二分查找,找到key在mHashes数组中的索引。

(2)通过索引找到对应的value值。

4、remove方法

public V remove(Object key) {        final int index = indexOfKey(key);        if (index >= 0) {            return removeAt(index);        }        return null;    }


  public V removeAt(int index) {        final Object old = mArray[(index << 1) + 1];        if (mSize <= 1) {            // Now empty.            if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");            freeArrays(mHashes, mArray, mSize);            mHashes = ContainerHelpers.EMPTY_INTS;            mArray = ContainerHelpers.EMPTY_OBJECTS;            mSize = 0;        } else {            if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {                // Shrunk enough to reduce size of arrays.  We don't allow it to                // shrink smaller than (BASE_SIZE*2) to avoid flapping between                // that and BASE_SIZE.                final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);                if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);                final int[] ohashes = mHashes;                final Object[] oarray = mArray;                allocArrays(n);                mSize--;                if (index > 0) {                    if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");                    System.arraycopy(ohashes, 0, mHashes, 0, index);                    System.arraycopy(oarray, 0, mArray, 0, index << 1);                }                if (index < mSize) {                    if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize                            + " to " + index);                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);                    System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,                            (mSize - index) << 1);                }            } else {                mSize--;                if (index < mSize) {                    if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize                            + " to " + index);                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);                    System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,                            (mSize - index) << 1);                }                mArray[mSize << 1] = null;                mArray[(mSize << 1) + 1] = null;            }        }        return (V)old;    }
(1)通过key找到在mHashes数组中对应的索引

(2)判断如果元素小于等于一个,将mHashes和mArray数组置空

(3)如果元素大于1个,将该索引之后的所有元素向前移一位。

如果ArrayMap容量过大(大于BASE_SIZE*2,持有数据不足),则降低ArrayMap的容量,较少内存占用。

总结:当数据量不大,插入删除不频繁,并且使用ArrayMap代替HashMap存储数据。




0 0
原创粉丝点击