Google推荐数据结构之SparseArray

google给安卓开发者推出了新型的Map存储集合,ArrayMap和SparseArray等。我们已经分析过了HashMap的实现原理(HashMap去重原理和内部实现),通过key的hash值把它放到数组的一个位置上,然后这个位置上形成一个链表结构,通过key的equals来判断相同。那google推出的有什么不同的地方吗?今天就让我们来看一看SparseArray到底是怎么回事。
其实SparseArray可以理解成一个键为int的map。内部有一个key的int型数组,一个value的object型数组,两个数组通过角标来形成对应关系。

public class SparseArray implements Cloneable {
    
    private static final Object DELETED = new Object();
    
    private boolean mGarbage = false;
    
    private int[] mKeys;
    
    private Object[] mValues;
    private int mSize;

    /**
     * Creates a new SparseArray containing no mappings.
     */
    public SparseArray() {
        this(10);
    }

    /**
     * Creates a new SparseArray containing no mappings that will not
     * require any additional memory allocation to store the specified
     * number of mappings.  If you supply an initial capacity of 0, the
     * sparse array will be initialized with a light-weight representation
     * not requiring any additional array allocations.
     */
    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;
    }
public void append(int key, E value) {
        if (mSize != 0 && key <= mKeys[mSize - 1]) {
        
            put(key, value);
            return;
        }
        
        
        if (mGarbage && mSize >= mKeys.length) {
            gc();
        }
        
        mKeys = GrowingArrayUtils.append(mKeys, mSize, key);
        mValues = GrowingArrayUtils.append(mValues, mSize, value);
        mSize++;
    }
 /**
     * Primitive int version of {@link #append(Object[], int, Object)}.
     */
    public static int[] append(int[] array, int currentSize, int element) {
        assert currentSize <= array.length;
        
        if (currentSize + 1 > array.length) {
        
            int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));
            System.arraycopy(array, 0, newArray, 0, currentSize);
            array = newArray;
        }
        
        array[currentSize] = element;
        return array;
    }

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

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

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

get方法也比较简单,大家看下就行了,不再分析。

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) {
            return valueIfKeyNotFound;
        } else {
            return (E) mValues[i];
        }
    }

indexOf方法,因为key是有序的,所以使用二分,values只能顺序遍历。

/**
    * Returns the index for which {@link #keyAt} would return the
    * specified key, or a negative number if the specified
    * key is not mapped.
    */
   public int indexOfKey(int key) {
       if (mGarbage) {
           gc();
       }

       return ContainerHelpers.binarySearch(mKeys, mSize, key);
   }

   /**
    * Returns an index for which {@link #valueAt} would return the
    * specified key, or a negative number if no keys map to the
    * specified value.
    * 

Beware that this is a linear search, unlike lookups by key, * and that multiple keys can map to the same value and this will * find only one of them. *

Note also that unlike most collections' {@code indexOf} methods, * this method compares values using {@code ==} rather than {@code equals}. */ public int indexOfValue(E value) { if (mGarbage) { gc(); } for (int i = 0; i < mSize; i++) if (mValues[i] == value) return i; return -1; }

clear也比较简单。

public void clear() {
       int n = mSize;
       Object[] values = mValues;

       for (int i = 0; i < n; i++) {
           values[i] = null;
       }

       mSize = 0;
       mGarbage = false;
   }

最后一个比较重要的是remove,里边控制了这个 mGarbage,然后是我们前边说的,不是真正删除,只是修改value的值,同时修改了mGarbage。

  /**
    * Alias for {@link #delete(int)}.
    */
   public void remove(int key) {
       delete(key);
   }

   /**
    * Removes the mapping at the specified index.
    *
    * 

For indices outside of the range 0...size()-1, * the behavior is undefined.

*/ public void removeAt(int index) { if (mValues[index] != DELETED) { mValues[index] = DELETED; mGarbage = true; } } /** * Remove a range of mappings as a batch. * * @param index Index to begin at * @param size Number of mappings to remove * *

For indices outside of the range 0...size()-1, * the behavior is undefined.

*/ public void removeAtRange(int index, int size) { final int end = Math.min(mSize, index + size); for (int i = index; i < end; i++) { removeAt(i); } } /** * Removes the mapping from the specified key, if there was any. */ public void delete(int key) { int i = ContainerHelpers.binarySearch(mKeys, mSize, key); if (i >= 0) { if (mValues[i] != DELETED) { mValues[i] = DELETED; mGarbage = true; } } }

获取size的时候因为它是伪删除,所以需要判断 mGarbage,需要的时候进行gc。

 /**
    * Returns the number of key-value mappings that this SparseArray
    * currently stores.
    */
   public int size() {
       if (mGarbage) {
           gc();
       }

       return mSize;
   }

到此为止,我们把这种map分析完了,对应的还有SparseBooleanArray, SparseIntArray, SparseLongArray只不过它们的value类型确定了下来,不过他们删除时是真正的删除,其它跟SparseArray完全一样。
还有一组 LongSparseArray,LongSparseLongArray跟这一组完全类似。

你可能感兴趣的:(Google推荐数据结构之SparseArray)