轉載自:http://blog.csdn.net/u010687392/article/details/47809295java
在Android開發時,咱們使用的大部分都是Java的api,好比HashMap這個api,使用率很是高,可是對於Android這種對內存很是敏感的移動平臺,不少時候使用一些java的api並不能達到更好的性能,相反反而更消耗內存,因此針對Android這種移動平臺,也推出了更符合本身的api,好比SparseArray、ArrayMap用來代替HashMap在有些狀況下能帶來更好的性能提高。android
介紹它們以前先來介紹一下HashMap的內部存儲結構,就明白爲何推薦使用SparseArray和ArrayMap算法
HashMap內部是使用一個默認容量爲16的數組來存儲數據的,而數組中每個元素卻又是一個鏈表的頭結點,因此,更準確的來講,HashMap內部存儲結構是使用哈希表的拉鍊結構(數組+鏈表),如圖:
這種存儲數據的方法叫作拉鍊法
且每個結點都是Entry類型,那麼Entry是什麼呢?咱們來看看HashMap中Entry的屬性:api
final K key; V value; final int hash; HashMapEntry<K, V> next;
從中咱們得知Entry存儲的內容有key、value、hash值、和next下一個Entry,那麼,這些Entry數據是按什麼規則進行存儲的呢?就是經過計算元素key的hash值,而後對HashMap中數組長度取餘獲得該元素存儲的位置,計算公式爲hash(key)%len,好比:假設hash(14)=14,hash(30)=30,hash(46)=46,咱們分別對len取餘,獲得
hash(14)%16=14,hash(30)%16=14,hash(46)%16=14,因此key爲1四、30、46的這三個元素存儲在數組下標爲14的位置,如:
從中能夠看出,若是有多個元素key的hash值相同的話,後一個元素並不會覆蓋上一個元素,而是採起鏈表的方式,把以後加進來的元素加入鏈表末尾,從而解決了hash衝突的問題,由此咱們知道HashMap中處理hash衝突的方法是鏈地址法,在此補充一個知識點,處理hash衝突的方法有如下幾種:數組
講到這裏,重點來了,咱們知道HashMap中默認的存儲大小就是一個容量爲16的數組,因此當咱們建立出一個HashMap對象時,即便裏面沒有任何元素,也要分別一塊內存空間給它,並且,咱們再不斷的向HashMap裏put數據時,當達到必定的容量限制時(這個容量知足這樣的一個關係時候將會擴容:HashMap中的數據量>容量*加載因子,而HashMap中默認的加載因子是0.75),HashMap的空間將會擴大,並且擴大後新的空間必定是原來的2倍,咱們能夠看put()方法中有這樣的一行代碼:數據結構
int newCapacity = oldCapacity * 2;
因此,重點就是這個,只要一知足擴容條件,HashMap的空間將會以2倍的規律進行增大。假如咱們有幾十萬、幾百萬條數據,那麼HashMap要存儲完這些數據將要不斷的擴容,並且在此過程當中也須要不斷的作hash運算,這將對咱們的內存空間形成很大消耗和浪費,並且HashMap獲取數據是經過遍歷Entry[]數組來獲得對應的元素,在數據量很大時候會比較慢,因此在Android中,HashMap是比較費內存的,咱們在一些狀況下可使用SparseArray和ArrayMap來代替HashMap。性能
SparseArray比HashMap更省內存,在某些條件下性能更好,主要是由於它避免了對key的自動裝箱(int轉爲Integer類型),它內部則是經過兩個數組來進行數據存儲的,一個存儲key,另一個存儲value,爲了優化性能,它內部對數據還採起了壓縮的方式來表示稀疏數組的數據,從而節約內存空間,咱們從源碼中能夠看到key和value分別是用數組表示:優化
private int[] mKeys; private Object[] mValues;
咱們能夠看到,SparseArray只能存儲key爲int類型的數據,同時,SparseArray在存儲和讀取數據時候,使用的是二分查找法,咱們能夠看看:ui
public void put(int key, E value) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
... } public E get(int key, E valueIfKeyNotFound) { int i = ContainerHelpers.binarySearch(mKeys, mSize, key); ... }
也就是在put添加數據的時候,會使用二分查找法和以前的key比較當前咱們添加的元素的key的大小,而後按照從小到大的順序排列好,因此,SparseArray存儲的元素都是按元素的key值從小到大排列好的。
而在獲取數據的時候,也是使用二分查找法判斷元素的位置,因此,在獲取數據的時候很是快,比HashMap快的多,由於HashMap獲取數據是經過遍歷Entry[]數組來獲得對應的元素。spa
public void put(int key, E value)
public void delete(int key)public void remove(int key)
其實remove內部仍是經過調用delete來刪除數據的
public E get(int key)
or
public E get(int key, E valueIfKeyNotFound)
該方法可設置若是key不存在的狀況下默認返回的value
在此以外,SparseArray還提供了兩個特有方法,更方便數據的查詢:
獲取對應的key:
public int keyAt(int index)
獲取對應的value:
public E valueAt(int index)
雖然說SparseArray性能比較好,可是因爲其添加、查找、刪除數據都須要先進行一次二分查找,因此在數據量大的狀況下性能並不明顯,將下降至少50%。
知足下面兩個條件咱們可使用SparseArray代替HashMap:
HashMap<Integer, Object> map = new HashMap<>(); 用SparseArray代替: SparseArray<Object> array = new SparseArray<>();
這個api的資料在網上能夠說幾乎沒有,然並卵,只能看文檔了
ArrayMap是一個<key,value>映射的數據結構,它設計上更多的是考慮內存的優化,內部是使用兩個數組進行數據存儲,一個數組記錄key的hash值,另一個數組記錄Value值,它和SparseArray同樣,也會對key使用二分法進行從小到大排序,在添加、刪除、查找數據的時候都是先使用二分查找法獲得相應的index,而後經過index來進行添加、查找、刪除等操做,因此,應用場景和SparseArray的同樣,若是在數據量比較大的狀況下,那麼它的性能將退化至少50%。
public V put(K key, V value)
public V get(Object key)
public V remove(Object key)
它和SparseArray同樣一樣也有兩個更方便的獲取數據方法:
public K keyAt(int index) public V valueAt(int index)
ArrayMap<Key, Value> arrayMap = new ArrayMap<>();
【注】:若是咱們要兼容aip19如下版本的話,那麼導入的包須要爲v4包
import android.support.v4.util.ArrayMap;
SparseArray和ArrayMap都差很少,使用哪一個呢?
假設數據量都在千級之內的狀況下:
一、若是key的類型已經肯定爲int類型,那麼使用SparseArray,由於它避免了自動裝箱的過程,若是key爲long類型,它還提供了一個LongSparseArray來確保key爲long類型時的使用
二、若是key類型爲其它的類型,則使用ArrayMap