安卓效能最佳化—使用ArrayMap與SparseArray

renke發表於2021-09-09

效能最佳化是我們做開發的必須要熟練掌握的技能,所以我打算寫一個效能最佳化專題,把平時用到的一些最佳化方法記錄下來,以便忘記的時候可以快速查詢,同時也給給其他開發者提供微薄之力吧:這篇文章講述的是在一些**特定的場景**使用使用ArrayMap與SparseArray代替HashMap,提高對資料的操作;先看看官方文件的描述:

ArrayMap is a generic key->value mapping data structure that is designed to be more memory efficient than a traditional HashMap, this implementation is a version of the platform's ArrayMap that can be used on older versions of the platform.SparseArrays map integers to Objects. Unlike a normal array of Objects, there can be gaps in the indices. It is intended to be more memory efficient than using a HashMap to map Integers to Objects, both because it avoids auto-boxing keys and its data structure doesn't rely on an extra entry object for each mapping.Note that for containers holding up to hundreds of items, the performance difference is not significant, less than 50%.

從官方文件可以看出使用ArrayMap與SparseArray都要比傳統的HashMap 更有效率;但是最後有一個note,也就是當資料量達到千級以上的時候,ArrayMap與SparseArray都要比傳統的HashMap 效率更低50%;

HashMap

HashMap允許使用 null 值和 null 鍵,是基於hashing原理,我們透過put()和get()方法儲存和獲取物件。HashMap的結構:

圖片描述

HashMap結構

HashMap 有兩個引數影響其效能:初始容量 和載入因子。容量是雜湊表中桶的數量(預設16組),初始容量只是雜湊表在建立時的容量。載入因子 是雜湊表在其容量自動增加之前可以達到多滿的一種尺度。當雜湊表中的條目數超出了載入因子與當前容量的乘積時,則要對該雜湊表進行 rehash 操作(即重建內部資料結構),從而雜湊表將具有大約兩倍的桶數。載入因子預設值為0.75 。當我們將鍵值對傳遞給put()方法時,它呼叫鍵物件的hashCode()方法來計算hashcode,讓後找到bucket位置來儲存值物件。當獲取物件時,透過鍵物件的equals()方法找到正確的鍵值對,然後返回值物件。HashMap使用LinkedList來解決碰撞問題,當發生碰撞了,物件將會儲存在LinkedList的下一個節點中。 HashMap在每個LinkedList節點中儲存鍵值對物件。

put()方法

圖片描述

當兩個Key同時hash到一個值時,就會出現這樣的衝突。這個衝突主要有2種解決方法。

1、開放地址,亦即如果hash衝突,則在空閒的位置進行插入

2、hash複用,同一個hash值,鏈式地加入多個value

get()方法


圖片描述

HashMap還有很多方法,這裡就不一一例舉了;


圖片描述

透過get與put的原始碼,可以看出HashMap獲取資料是透過遍歷Entry[]陣列來得到對應的元素,在資料量很大時候會比較慢,所以在Android中,HashMap是比較費記憶體的,我們在一些情況下可以使用SparseArray和ArrayMap來代替HashMap。

SparseArray


圖片描述

可以看出SparseArray由兩個陣列mKeys和mValues存放;其中key的型別為int型,這就顯得SparseArray比HashMap更省記憶體一些,SparseArray在儲存和讀取資料時候,使用的是二分查詢法,那何為二分法呢?

先看一下put()方法:


圖片描述

在上面程式碼中有一個ContainerHelpers.binarySearch(mKeys, mSize, key)方法,這是Arrays提供了一個方便查詢的方法,也就是我們所說的“二分法”;


圖片描述

get()方法:


圖片描述

使用二分查詢法和之前的key比較當前我們新增的元素的key的大小,然後按照從小到大的順序排列好,所以,SparseArray儲存的元素都是按元素的key值從小到大排列好的。而在獲取資料的時候,也是使用二分查詢法判斷元素的位置,所以,在獲取資料的時候非常快,比HashMap快的多,因為HashMap獲取資料是透過遍歷Entry[]陣列來得到對應的元素。

其他的一些方法:


圖片描述

ArrayMap

ArrayMap是一個鍵值對對映的資料結構,它設計上更多的是考慮記憶體的最佳化,內部是使用兩個陣列進行資料儲存,一個陣列記錄key的hash值,另外一個陣列記錄Value值,它和SparseArray一樣,也會對key使用二分法進行從小到大排序,區別是ArrayMap的key是hash值,


圖片描述

可以看出ArrayMap的構造方法直接呼叫的父類的構造方法:


圖片描述

put()方法:


圖片描述

get()方法與SparseArray的一樣,就不哆嗦了:

總結

因為ArrayMap與SparseArray內部都使用了二分法進行從小到大的排序,所以當資料量很大的時候,效率至少降低一半,所以谷歌推薦資料量在千級以內時使用ArrayMap與SparseArray,資料量非常大時使用HashMap;



作者:lin_林
連結:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/430/viewspace-2809898/,如需轉載,請註明出處,否則將追究法律責任。

相關文章