1. 程式人生 > >ArrayMap和HashMap的記憶體佔用的區別

ArrayMap和HashMap的記憶體佔用的區別

HashMap 原理:

    HashMap 是基於雜湊表的 Map 介面實現的,內部儲存的結構是使用雜湊表的拉鍊結構(陣列+連結串列)的方式,如下圖所示

     

HashMap中預設的儲存大小就是一個容量為16的陣列,所以當我們創建出一個HashMap物件時,即使裡面沒有任何元素,也要分別一塊記憶體空間給它,而且,

我們再不斷的向HashMap裡put資料時,當達到一定的容量限制時(這個容量滿足這樣的一個關係時候將會擴容:HashMap中的資料量>容量*載入因子,

HashMap中預設的載入因子是0.75),HashMap的空間將會擴大,而且擴大後新的空間大約是原來的2倍,只要一滿足擴容條件,HashMap的

空間將會以2

的規律進行增大。假如我們有幾十萬、幾百萬條資料,那麼HashMap要儲存完這些資料將要不斷的擴容,在此過程中也需要不斷的做hash運算,這將對我們

存空間造成很大消耗和浪費。

用pixel手機做測試,儲存10萬條資料方法如下:

                 for (int i = 0; i <100000; i++) {

                    hashMap.put(i,"sunchao = "+i);

                }

使用for迴圈,儲存10萬條資料,我們可以檢視,當前測試APP的記憶體動態圖如下:

 測試HashMap儲存10萬條資料得出的記憶體動態圖: 

從抓取的APP的動態的圖,我們可以發現,HashMap的消耗的最大記憶體達到了103.5MB

ArrayMap的工作原理:

為了解決HashMap佔記憶體的弊端,Android提供了記憶體效率更高的ArrayMap。它內部使用兩個陣列進行工作,其中一個數組記錄

key換成hash值過後的順序列表,另外一個數組按key的順序記錄Key-Value值,如下圖所示

可以看出ArrayMap採用的是Key-Values對映資料結構。

ArrayMap中主要儲存的資料的是兩個資料

mHashs中儲存出的是每個key的hash值,並且在這些key的hash值在陣列當中是從小到大排序的。mArray的陣列長度是mHashs的兩倍,每兩個元素分別

是key和value,這兩元素對應mHashs中的hash值。在我們使用put方法進行儲存資料的過程中,空間不夠時,會發生如下擴容

BASE_SIZE = 4,先判斷oSize值是否大於等於8,如果是則n=oSize*1.5,否則就判斷是否大於等於4,是則n=8個,否則n=4個。

然後把老的陣列中的資料複製到了新的陣列當中如下

allocArrays和freeArrays方法中,。這兩個方法的作用基本上就是當長度不夠用,我們需要廢棄掉老的陣列,使用新的陣列的時候,

把老的陣列(包含mHashes和mArray)的資料新增到oArray當中,然後把oldArray賦值給mBaseCache(4個長度),如果再有

新的ArrayMap建立陣列空間的時候,如果還是申請4個的空間,那麼優先使用快取下來的這個。

用pixel手機做測試,同樣儲存10萬條資料方法如下:

                 for (int i = 0; i <100000; i++) {

                    arrayMap.put(i,"sunchao = "+i);

                }

使用for迴圈,儲存10萬條資料,我們可以檢視,當前測試APP的記憶體動態圖如下:

測試ArrayMap儲存10萬條資料得出的記憶體動態圖: 

從抓取的APP的動態的圖,我們可以發現,ArrayMap的消耗的最大記憶體達到了91.4MB

從中可以得出結論: 在儲存資料方面ArrayMap確實要比HashMap消耗的記憶體小。HashMap初始值16個長度,每次擴容的時候,

直接申請雙倍的陣列空間。ArrayMap每次擴容的時候,如果size長度大於8時申請size*1.5個長度,大於4小於8時申請8個,小於

4時申請4個。ArrayMap其實是申請了更少的記憶體空間,但是擴容的頻率會更高,並且ArrayMap採用了一種獨特的方式,能夠重複

的利用因為資料擴容而遺留下來的陣列空間,而HashMap沒有這種設計。