Android應用性能優化之使用SparseArray替代HashMap

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

 

 

 注:

文中關於稀疏數組(Sparse array)的定義說明參照至:

http://hi.baidu.com/piaopiao_0423/item/d8cc2b99729f8380581461d1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章