天天看點

HashMap SparseArray ArrayMap

程式記憶體的管理是否合理高效對應用的性能有着很大的影響,有的時候對容器的使用不當也會導緻記憶體管理效率低下。Android為移動作業系統特意編寫了一些更加高效的容器,例如SparseArray,ArrayMap。

我們經常會使用到HashMap這個容器,它非常好用,但是卻很占用記憶體。

HashMap

HashMap内部是使用一個預設容量為16的數組來存儲資料的,而數組中每一個元素卻又是一個連結清單的頭結點,是以,更準确的來說,HashMap内部存儲結構是使用哈希表的拉鍊結構(數組+連結清單)這種存儲資料的方法叫做拉鍊法 

我們知道HashMap中預設的存儲大小就是一個容量為16的數組,是以當我們建立出一個HashMap對象時,即使裡面沒有任何元素,也要分别一塊記憶體空間給它,而且,我們再不斷的向HashMap裡put資料時,當達到一定的容量限制時(這個容量滿足這樣的一個關系時候将會擴容:HashMap中的資料量>容量*加載因子,而HashMap中預設的加載因子是0.75),HashMap的空間将會擴大,而且擴大後新的空間一定是原來的2倍,我們可以看put()方法中有這樣的一行代碼:

int newCapacity = oldCapacity * 2;
           

是以,重點就是這個,隻要一滿足擴容條件,HashMap的空間将會以2倍的規律進行增大。假如我們有幾十萬、幾百萬條資料,那麼HashMap要存儲完這些資料将要不斷的擴容,而且在此過程中也需要不斷的做hash運算,這将對我們的記憶體空間造成很大消耗和浪費,而且HashMap擷取資料是通過周遊Entry[]數組來得到對應的元素,在資料量很大時候會比較慢, 為了解決HashMap更占記憶體的弊端,Android提供了記憶體效率更高的SparseArray和ArrayMap。它内部使用兩個數組進行工作,其中一個數組記錄key hash過後的順序清單,另外一個數組按key的順序記錄Key-Value值。

SparseArray

SparseArray比HashMap更省記憶體,在某些條件下性能更好,主要是因為它避免了對key的自動裝箱(int轉為Integer類型),它内部則是通過兩個數組來進行資料存儲的,一個存儲key,另外一個存儲value,為了優化性能,它内部對資料還采取了壓縮的方式來表示稀疏數組的資料,進而節約記憶體空間,我們從源碼中可以看到key和value分别是用數組表示:

private int[] mKeys;
    private Object[] mValues;
           

我們可以看到,SparseArray隻能存儲key為int類型的資料,同時,SparseArray在存儲和讀取資料時候,使用的是二分查找法,也就是在put添加資料的時候,會使用二分查找法和之前的key比較目前我們添加的元素的key的大小,然後按照從小到大的順序排列好,是以,SparseArray存儲的元素都是按元素的key值從小到大排列好的。 

而在擷取資料的時候,也是使用二分查找法判斷元素的位置,是以,在擷取資料的時候非常快,比HashMap快的多,因為HashMap擷取資料是通過周遊Entry[]數組來得到對應的元素。

SparseArray應用場景:

資料量不大,最好在千級以内

key必須為int類型,這中情況下的HashMap可以用SparseArray代替:

ArrayMap

當你想擷取某個value的時候,ArrayMap會計算輸入key轉換過後的hash值,然後對hash數組使用二分查找法尋找到對應的index,然後我們可以通過這個index在另外一個數組中直接通路到需要的鍵值對。如果在第二個數組鍵值對中的key和前面輸入的查詢key不一緻,那麼就認為是發生了碰撞沖突。為了解決這個問題,我們會以該key為中心點,分别上下展開,逐個去對比查找,直到找到比對的值。

随着數組中的對象越來越多,查找通路單個對象的花費也會跟着增長,這是在記憶體占用與通路時間之間做權衡交換。

ArrayMap的插入與删除的效率是不夠高的,但是如果數組的清單隻是在一百這個數量級上,則完全不用擔心這些插入與删除的效率問題。

與HashMap相比,ArrayMap在循環周遊的時候也更加簡單高效

//ArrayMap
        for (int i=0;i<map.size();i++){
            Object keyAt=map.keyAt(i);
            Object valueAt=map.valueAt(i);
            ...
        }
       
        //HashMap
        for(Iterator it=map.keySet().iterator();it.hasNext();){
            Object next = it.next();
            ...
        };
           

ArrayMap應用場景:

前面講了很多ArrayMap的優點,但并不是所有情況下都适合使用ArrayMap,我們應該在滿足下面2個條件的時候才考慮使用ArrayMap:

對象個數的數量級最好是千以内;

資料組織形式包含Map結構。

SparseArray和ArrayMap

假設資料量都在千級以内的情況下:

1、如果key的類型已經确定為int類型,那麼使用SparseArray,因為它避免了自動裝箱的過程,
如果key為long類型,它還提供了一個LongSparseArray來確定key為long類型時的使用

2、如果key類型為其它的類型,則使用ArrayMap