標籤:
HashMap是java裡比較常用的一個集合類,我比較習慣用來緩衝一些處理後的結果。最近在做一個Android項目,在代碼中定義這樣一個變數,執行個體化時,Eclipse卻給出了一個 performance 警告。
意思就是說用SparseArray<E>來替代,以擷取更好效能。老實說,對SparseArray並不熟悉,第一感覺應該是Android提供的一個類。按住Ctrl點擊進入SparseArray的源碼,果不其然,確定是Android提供的一個工具類。
單純從字面上來理解,SparseArray指的是稀疏數組(Sparse array),所謂稀疏數組就是數組中大部分的內容值都未被使用(或都為零),在數組中僅有少部分的空間使用。因此造成記憶體空間的浪費,為了節省記憶體空間,並且不影響數組中原有的內容值,我們可以採用一種壓縮的方式來表示稀疏數組的內容。
假設有一個9*7的數組,其內容如下:
在此數組中,共有63個空間,但卻只使用了5個元素,造成58個元素空間的浪費。以下我們就使用稀疏數組重新來定義這個數組:
其中在稀疏數組中第一部分所記錄的是原數組的列數和行數以及元素使用的個數、第二部分所記錄的是原數組中元素的位置和內容。經過壓縮之後,原來需要聲明大小為63的數組,而使用壓縮後,只需要聲明大小為6*3的數組,僅需18個儲存空間。
繼續閱讀SparseArray的源碼,從構造方法我們可以看出,它和一般的List一樣,可以預先設定容器大小,預設的大小是10:
public SparseArray() { this(10); } public SparseArray(int initialCapacity) { initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); mKeys = new int[initialCapacity]; mValues = new Object[initialCapacity]; mSize = 0; }
再來看看它對資料的“增刪改查”。
它有兩個方法可以添加索引值對:
public void put(int key, E value) {} public void append(int key, E value){}
有四個方法可以執行刪除操作:
public void delete(int key) {} public void remove(int key) {} //直接調用的delete(int key) public void removeAt(int index){} public void clear(){}
修改資料起初以為只有setValueAt(int index, E value)可以修改資料,但後來發現put(int key, E value)也可以修改資料,我們查看put(int key, E value)的源碼可知,在put資料之前,會先尋找要put的資料是否已經存在,如果存在就是修改,不存在就添加。
public void put(int key, E value) { int i = binarySearch(mKeys, 0, 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 = ~binarySearch(mKeys, 0, mSize, key); } …………
所以,修改資料實際也有兩種方法:
public void put(int key, E value) public void setValueAt(int index, E value)
最後再來看看如何尋找資料。有兩個方法可以查詢取值:
public E get(int key) public E get(int key, E valueIfKeyNotFound)
其中get(int key)也只是調用了 get(int key,E valueIfKeyNotFound),最後一個從傳參的變數名就能看出,傳入的是找不到的時候返回的值.get(int key)當找不到的時候,預設返回null。
查看第幾個位置的鍵:
public int keyAt(int index)
有一點需要注意的是,查看鍵所在位置,由於是採用二分法尋找鍵的位置,所以找不到時返回小於0的數值,而不是返回-1。返回的負值是表示它在找不到時所在的位置。
查看第幾個位置的值:
public E valueAt(int index)
查看值所在位置,沒有的話返回-1:
public int indexOfValue(E value)
最後,發現其核心就是折半尋找函數(binarySearch),演算法設計的很不錯。
private static int binarySearch(int[] a, int start, int len, int key) { int high = start + len, low = start - 1, guess; while (high - low > 1) { guess = (high + low) / 2; if (a[guess] < key) low = guess; else high = guess; } if (high == start + len) return ~(start + len); else if (a[high] == key) return high; else return ~high; }
相應的也有SparseBooleanArray,用來取代HashMap<Integer, Boolean>,SparseIntArray用來取代HashMap<Integer, Integer>,大家有興趣的可以研究。
總結:SparseArray是android裡為<Interger,Object>這樣的Hashmap而專門寫的類,目的是提高效率,其核心是折半尋找函數(binarySearch)。在Android中,當我們需要定義
HashMap<Integer, E> hashMap = new HashMap<Integer, E>();
時,我們可以使用如下的方式來取得更好的效能.
SparseArray<E> sparseArray = new SparseArray<E>();
Android應用效能最佳化之使用SparseArray替代HashMap