Android容器類小結
相較於其他裝置,移動裝置有自己的特點,記憶體小是一個很突出的問題,Google針對android裝置的這一特點,開發了一套容器框架,目的就是為了更加高效地利用記憶體。接下來就對這些容器進行一下總結。
組織結構

以上是android中容器的實現繼承結構,簡單梳理一下:
-
ArraySet
實現了Set
和Collections
介面,在api 23中新增 -
ArrayMap
實現了Map
介面,在api 19中新增 -
SparseArray
,SparseIntArray
,SparseBooleanArray
實現了Cloneable
介面,在api 1中新增 -
SparseLong
實現了Cloneable
介面,在api 18中新增
分類
從** 功能
**上劃分,可以將以上容器劃分為兩類:
-
儲存元素
ArraySet
優化了HashSet
對元素的儲存 -
儲存鍵值對 相較於
HashMap
,具體的優化方向如下:ArrayMap
優化了HashMap
儲存Object --> Object
的鍵值儲存;SparseArray
優化了int --> Object
的鍵值儲存;SparseIntArray
優化了int --> int
的鍵值儲存;SparseBooleanArray
優化了int --> boolean
的鍵值儲存;SparseLongArray
優化了int --> long
的鍵值儲存。
優化方法
從組織結構可以看出,可以將這些容器分為3類: ArraySet
, ArrayMap
和剩餘的容器。通過前面的分析可以知道, ArraySet
和 ArrayMap
使用的相同的優化方式, SparseArray
在進行優化的時候使用 gc
垃圾回收策略,故從優化方法上進行分類的話可以分一下三類:
-
ArraySet
,ArrayMap
使用陣列mKeys
儲存key
的hash值,hash值在mKeys
的位置為index
,並將value儲存到mValues
陣列對應下標的位置(ArrayMap
中key
和value
分別在mValues
的index * 2
和index * 2 + 1
的位置)。查詢或者修改元素時,使用二分查詢在mKeys
中找到元素在mValues
的下標,然後進行修改或者返回。 -
SparseArray
使用int
型別的mKeys
陣列儲存int
型別的鍵,下標為index
,將Object
型別的value
儲存在在Object
型別的陣列mValues
的index
位置,在查詢和修改時,使用二分查詢在mKeys
中找到元素在mValues
的下標,然後進行修改或者返回。在刪除value
時,SparseArray
並不直接進行陣列元素的移動,而是將待刪除的value
標記為DELETED
狀態,在gc
的過程中將所有非DELETED
狀態的元素移動到陣列的最前面,從而減少二分查詢的時間。 -
SparseIntArray
,SparseLongArray
,SparseBooleanArray
這3個容器可以理解成專用容器,使用int
型別陣列和對應型別的陣列;使用二分查詢快速查詢元素,然後進行刪除,修改,新增操作。
優化共同點與差異
雖然這些容器儲存的元素型別不同,但是通過分析可以發現他們在記憶體優化中的共同點,接下來就分析下這些容器在優化上存在的共同點和差異。
共同點
-
資料結構
ArrayMap
中mValues的長度是mKeys的2倍,但也僅僅是陣列長度上的差異,底層儲存使用的思想仍然是一樣的;int
型別的陣列mKeys
裡的元素時按照升序進行排列的。相較於HashMap
使用Node
結構儲存,這樣的儲存方式使用更小的儲存空間儲存k-v
,同時避免了原始資料型別的自動裝箱。
-
查詢方法 在組織結構中列出的容器,他們在進行元素查詢時,都會先在
mKeys
陣列中利用二分查詢找到元素的下標index
,然後使用index
到mValues
陣列中對value
進行操作。 -
獲取帶插入下標 在進行元素插入時,會首先使用二分查詢在
mKeys
陣列中查詢元素的下標,如果元素不存在,則二分查詢會返回元素待插入位置的取反。
不同點
- 對
key
的處理ArraySet
,ArrayMap
底層實現時,會計算待插入元素的hash值,根據hash值,在mKeys
找到待插入位置;SparseArray
和SparseXXXArray
儲存的時候直接使用key
值,不會進行hash計算。 - 對
null
的處理ArraySet
和ArrayMap
允許插入key
為null
的元素,key
的hash值為0;SparseArray
和SparseXXXArray
儲存的時由於直接使用int型別的資料作為key
,故不存在key
為null
的情況。 - 快取 為了避免頻繁的記憶體回收,
ArraySet
和ArrayMap
添加了快取結構,SparseArray
和SparseXXXArray
沒有快取 - 擴容規則
ArraySet
和ArrayMap
在進行擴容的時,容量的變化規則為4, 8 , size * 2 / 3
,SparseArray
和SparseXXXArray
使用ArrayUtils.newUnpaddedArray
建立新的資料,將原來的資料拷貝到新陣列中。
使用建議
雖然這些容器在Android裝置上可以更高效地利用記憶體,但是還是存在使用使用限制。
ArrayMap HashMap
總結
前面說了很多,其實android容器優化的根本思想就是使用 int
到其他型別的對映,使用陣列儲存著兩個對映,用以優化 HashMap
對 k-v
的儲存。這種優化適用於元素數量較少(少於1000)的情況。