1. 程式人生 > >SparseArray具體解釋,我說SparseArray,你說要!

SparseArray具體解釋,我說SparseArray,你說要!

serializa 基礎上 diff get 依據 rip 數組 find 普通

可能在Android 中使用HashMap 的時候看到過提示。
HashMap<Integer,Bitmap> mp = new HashMap<Integer,Bitmap>();
提示:Use new SparseArray<Bitmap>(...) instead for better performance意思是,使用 SparseArray 將獲得更好的性能
(註:這個提示我再eclipse 中見過,而在studio 中並沒有看到過這種提示)
那麽SparseArray這個類是幹嘛使得,有什麽長處,特性呢?
ok。我們從以下幾點介紹下SparseArray 這個類。

1.武功秘籍之SparseArray(SparseArray文檔介紹)

SparseArrays map integers to Objects. Unlike a normal array of Objects, there can be gaps in the indices. It is intended to be more memory efficient than using a HashMap to map Integers to Objects, both because it avoids auto-boxing keys and its data structure doesn’t rely on an extra entry object for each mapping.

(譯文+個人理解)
SparseArrays不同於普通的對象數組。there can be gaps in the indices.(指針中能夠存在空白?註:這句我不太理解,不知道怎樣翻譯,是說SparseArrays 是不連續存儲?)。它的目的是比使用從整數映射對象的HashMap(簡單來說就是 HashMap<Integer,Object>)有更有效的使用內存。

同一時候也避免auto-boxing(自己主動裝箱)(註:auto-boxing(自己主動裝箱):將原始類型封裝為對象類型。比方把int類型封裝成Integer類型。)和數據結構對每條映射不依賴額外的Entry 對象(註:這裏是和HashMap做的對照。在HashMap有須要引入Entry<K,V>

,而SparseArrays比較簡單,裏面是兩個一維數組 int[] mKeys; 和 Object[] mValues)

Note that this container keeps its mappings in an array data structure, using a binary search to find keys. The implementation is not intended to be appropriate for data structures that may contain large numbers of items. It is generally slower than a traditional HashMap, since lookups require a binary search and adds and removes require inserting and deleting entries in the array. For containers holding up to hundreds of items, the performance difference is not significant, less than 50%.

註意,這個容器在數組數據結構中維持一個映射關系。使用二分查找法來找到key.它的目標不是為了適應數據結構中包括大量數據的情況。

通常情況下要比傳統的HashMap慢,由於查找是用二分查找法搜索,加入和刪除須要對數組進行加入和刪除。

對於有幾百條的數據的容器。性能差異不大,不超過50%(註:這裏我理解的是, SparseArrays 的優勢更體如今小數量上)

To help with performance, the container includes an optimization when removing keys: instead of compacting its array immediately, it leaves the removed entry marked as deleted. The entry can then be re-used for the same key, or compacted later in a single garbage collection step of all removed entries. This garbage collection will need to be performed at any time the array needs to be grown or the the map size or entry values are retrieved.

為了提高性能,該容器提供了一個優化:當刪除key鍵時。不是立刻刪除這一項,而是留下須要刪除的選項給一個刪除的標記。

該條目能夠被又一次用於同樣的key,或者被單個垃圾收集器逐步刪除完所有的條目後壓縮。在不論什麽時候。當數組須要增長(註:這裏我理解為 put、append之類的操作)或者Map 的長度、entry的value須要被檢索的時候,該垃圾收集器就會運行(註:這裏能夠從代碼中發現。google在相應的方法中調用 gc() 方法)。

It is possible to iterate over the items in this container using
* [email protected] #keyAt(int)} and [email protected] #valueAt(int)}. Iterating over the keys using
* keyAt(int) with ascending values of the index will return the
* keys in ascending order, or the values corresponding to the keys in ascending
* order in the case of valueAt(int).

在此使用此容器時,能夠叠代遍歷當中的item.. keyAt(int) 返回升序下的key .
valueAt(int) 返回升序狀態下。key 相應的value值。

2.SparseArray,HashMap 不服?SOLO

2.1 key,value 類型的比較

HashMap                   key:隨意類型         value:隨意類型
SparseArray               key:Integer         value:隨意類型 
SparseBooleanArray        key:Integer         value:Boolean類型。    
SparseIntArray            key:Integer         value:Integer類型。    
LongSparseArray           key:Long            value:隨意類型

依據需求的不同,找到合適的方法才是上上策。

2.2 內部存儲的比較

SparseArray  :int[] mKeys 和 Object[] mValues 來存儲 key 和 value 
HashMap      :內部存儲須要用到 `Entry<K,V>`

2.3 運行速度比較

這裏不打算做代碼的介紹了。這篇文章裏有比較具體的介紹,這裏我僅僅打算總結下。(http://www.open-open.com/lib/view/open1402906434918.html)

  1. 創建數據
    以10000條數據為例。HashMap用去約13.2M內存,SparseArray共用去 8.626M內存。

  2. 數據插入
    在正序插入數據時候,SparseArray比HashMap要快一些;HashMap無論是倒序還是正序開銷差點兒是一樣的。可是SparseArray的倒序插入要比正序插入要慢很多,為什麽呢?
    原因:SparseArray在檢索數據的時候使用的是二分查找,所以每次插入新數據的時候SparseArray都須要又一次排序,所以逆序是最差情況。

  3. 數據檢索
    SparseArray中存在須要檢索的下標時。SparseArray的性能略勝一籌可是當檢索的下標比較離散時,SparseArray須要使用多次二分檢索,性能顯然比hash檢索方式要慢一些了。

  4. 接口實現
    HashMap實現了Cloneable, Serializable SparseArray 實現了Cloneable 接口,也就是說SparseArray 是不支持序列化的。

3.代碼解析-功法傳授(Q增 W刪 E改 R查)

先天優越之—>初始技能

 /**
  * Creates a new SparseArray containing no mappings.
  * 創建默認容器大小為10的SparseArray
  */
public SparseArray() {
    this(10);
}

public SparseArray(int initialCapacity) {...}

如虎添翼之 —>

/**
 * Adds a mapping from the specified key to the specified value,
 * replacing the previous mapping from the specified key if there
 * was one.
通過指定的key和value加入一個鍵值對,假設這個位置已經存在一個了,則替換掉
 */
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++;
    }
}

/**
 * Puts a key/value pair into the array, optimizing for the case where
 * the key is greater than all existing keys in the array.
通過指定的key和value加入一個鍵值對,在原有的基礎上添加
 */
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++;
}

割袍斷義之 —>

/**
 * Removes the mapping from the specified key, if there was any.
 * 從鍵值對中刪除指定的key
 */
public void delete(int key) {
    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

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

/**
 * @hide
 * 隱藏方法
 * Removes the mapping from the specified key, if there was any, returning the old value.
 * 從鍵值對中刪除指定的key,假設在不論什麽地方還實用到,會返回舊值。

*/ public E removeReturnOld(int key) { int i = ContainerHelpers.binarySearch(mKeys, mSize, key); if (i >= 0) { if (mValues[i] != DELETED) { final E old = (E) mValues[i]; mValues[i] = DELETED; mGarbage = true; return old; } } return null; } /** * Alias for [email protected] #delete(int)}. (註意:這裏是調用的delete 的方法,所以remove方法和 delete效果是一樣的) */ public void remove(int key) { delete(key); } /** * Removes the mapping at the specified index. * 刪除指定index(註意這裏不是刪除指定的key,可是事實上我們能夠發現這個實現是和delete方法是一樣的) */ 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 index 開始刪除的位置 * @param size Number of mappings to remove 刪除的長度 */ public void removeAtRange(int index, int size) { final int end = Math.min(mSize, index + size); for (int i = index; i < end; i++) { removeAt(i); } }

偷梁換柱之—>

/**
 * Returns the index for which [email protected] #keyAt} would return the
 * specified key, or a negative number if the specified
 * key is not mapped.
 * 返回key 相應的index,假設指定的key沒有找到則返回一個負數
 */
public int indexOfKey(int key) {
    if (mGarbage) {
        gc();
    }

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

/**
 * 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.
改動指定index 下相應的value 值(註意這裏 第一個參數是index,不是 key。能夠配合indexOfKey(int key)先獲得key所在的index)
 */
public void setValueAt(int index, E value) {
    if (mGarbage) {
        gc();
    }

    mValues[index] = value;
}

按圖索驥之—>

/**
 * Gets the Object mapped from the specified key, or <code>null</code>
 * if no such mapping has been made.
通過指定的key獲取相應的Object,假設沒有找到相應的鍵值對,會默認返回null 
 */
public E get(int key) {
    return get(key, null);
}

/**
 1. Gets the Object mapped from the specified key, or the specified Object
 2. if no such mapping has been made.
 3. 通過運行的key獲取相應的Object,,假設沒有找到相應的鍵值對。會返回設置的默認值 
 */
@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];
    }
}

尋名劍(鍵) —>獲得key

/**
獲得指定index 下的key
 * */
public int keyAt(int index) {
    if (mGarbage) {
        gc();
    }
    return mKeys[index];
}

劍鞘—>獲得value

/**
 * 獲得指定index 相應下的value
 * */

public E valueAt(int index) {
    if (mGarbage) {
        gc();
    }

    return (E) mValues[index];
}

一個蘿蔔一個坑 —>*依據value 獲得index*

/**
 * 依據指定的value 獲得所在的index,假設沒有 則返回-1
 */

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

    for (int i = 0; i < mSize; i++)
        if (mValues[i] == value)
            return i;

    return -1;
}

終極必殺之受死吧妖孽,打回原形 —>clear()

/**
 * Removes all key-value mappings from this SparseArray.
 * 從SparseArray 中移除所有的key-value 鍵值對
 */
public void clear() {
    int n = mSize;
    Object[] values = mValues;

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

    mSize = 0;
    mGarbage = false;
}

SparseArray具體解釋,我說SparseArray,你說要!