HashMap是java里比較常用的一個集合類,我們一般用來緩存一些處理後的結果。但當你做一個Android項目時,在代碼中定義這樣一個變數,實例化時,Eclipse卻給出了一個 performance 警告。 意思是說Map已經不用了,使用SparseArray<Object>代替,以獲取更好性能 ...
HashMap是java里比較常用的一個集合類,我們一般用來緩存一些處理後的結果。但當你做一個Android項目時,在代碼中定義這樣一個變數,實例化時,Eclipse卻給出了一個 performance 警告。
意思是說Map已經不用了,使用SparseArray<Object>代替,以獲取更好性能。為什麼用SparseArray呢,單從字面意思,SparseArray就是稀疏數組
所謂稀疏數組就是數組中大部分的內容值都未被使用(或都為零),在數組中僅有少部分的空間使用。因此造成記憶體空間的浪費,為了節省記憶體空間,並且不影響數組中原有的內容值,我們可以採用一種壓縮的方式來表示稀疏數組的內容。
假設有一個9*7的數組,其內容如下:
圖 1 二維數組示例
在此數組中,共有63個空間,但卻只使用了5個元素,造成58個元素空間的浪費。以下我們就使用稀疏數組重新來定義這個數組:
圖 2 使用稀疏數組進行壓縮
其中在稀疏數組中第一部分所記錄的是原數組的列數和行數以及元素使用的個數、第二部分所記錄的是原數組中元素的位置和內容。經過壓縮之後,原來需要聲明大小為63的數組,而使用壓縮後,只需要聲明大小為6*3的數組,僅需18個存儲空間。
我們再來看看源碼中SparseArray到底做了哪些事情:
類結構
繼續閱讀SparseArray的源碼,從構造方法我們可以看出,它和一般的List一樣,可以預先設置容器大小,預設的大小是10:
也可以自定義容量
再來看看它對數據的“增刪改查”。
它有兩個方法可以添加鍵值對:
有四個方法可以執行刪除操作:
修改數據起初以為只有setValueAt(int index, E value)可以修改數據,但後來發現put(int key, E value)也可以修改數據,我們查看put(int key, E value)的源碼可知,在put數據之前,會先查找要put的數據是否已經存在,如果存在就是修改,不存在就添加。
所以,修改數據實際也有兩種方法:
最後再來看看如何查找數據。有兩個方法可以查詢取值:
其中get(int key)也只是調用了 get(int key,E valueIfKeyNotFound),最後一個從傳參的變數名就能看出,傳入的是找不到的時候返回的值.get(int key)當找不到的時候,預設返回null。
查看第幾個位置的鍵:
有一點需要註意的是,查看鍵所在位置,由於是採用二分法查找鍵的位置,所以找不到時返回小於0的數值,而不是返回-1。返回的負值是表示它在找不到時所在的位置。
查看第幾個位置的值:
查看值所在位置,沒有的話返回-1:
最後,發現其核心就是折半查找函數(binarySearch),演算法設計的很不錯。
相應的也有SparseBooleanArray,用來取代HashMap<Integer, Boolean>,SparseIntArray用來取代HashMap<Integer, Integer>,大家有興趣的可以研究。
總結:SparseArray是Android里為<Interger,Object>這樣的Hashmap而專門寫的類,目的是提高記憶體效率,其核心是折半查找函數(binarySearch)。註意記憶體二字很重要,因為它僅僅提高記憶體效率,而不是提高執行效率,所以也決定它只適用於Android系統(記憶體對android項目有多重要,地球人都知道)。SparseArray有兩個優點:1.避免了自動裝箱(auto-boxing),2.數據結構不會依賴於外部對象映射。我們知道HashMap 採用一種所謂的“Hash 演算法”來決定每個元素的存儲位置,存放的都是數組元素的引用,通過每個對象的hash值來映射對象。而SparseArray則是用數組數據結構來保存映射,然後通過折半查找來找到對象。但其實一般來說,SparseArray執行效率比HashMap要慢一點,因為查找需要折半查找,而添加刪除則需要在數組中執行,而HashMap都是通過外部映射。但相對來說影響不大,最主要是SparseArray不需要開闢記憶體空間來額外存儲外部映射,從而節省記憶體。