SparseArray代替HashMap
相信大家都明白,手機軟件的開發不同于PC軟件的開發,因為手機性能相對有限,內存也有限,所謂“寸土寸金”,可能稍有不慎,就會導致性能的明顯降低。Android為了方便開發者,特意在android.util這個包中提供了幾個提高效率的工具類,比如之前用過的LruCache類,這次我們來談談其他工具類,SparseArray,SparseBooleanArray和?SparseIntArray。
?
總體說,它們都是類似map這樣key-value的存儲方式,但是由于查找的算法不一樣。因此效率也各不同。但要明白,沒有說哪個一定是最好的。只有根據不同需求在不同場景去應用,才能獲取較優的結果。下面我們來看看它們的“廬山真面目”吧。
?
SparseArray
本來想在這里好好介紹何為SparseArray。但看完源碼和官方的文檔,發現里面已經介紹的很仔細了。于是決定將源碼中開始關于介紹SparseArray的那一段翻譯在這里,最后總計幾個要點。英文水平有限,翻譯不恰當的地方請先見諒。
package?android.util;??import?com.android.internal.util.ArrayUtils;??/**?*?SparseArrays?利用integer去管理object對象。不像一個正常的object對象數組,它能在索引數中快速的查找到所需的結果。(這?*?句話是音譯,原意是能在眾多索引數中“撕開一個缺口”,為什么原文這么表達?下面會慢慢說清楚。)它比HashMap去通過Integer索引?*?查找object對象時在內存上更具效率,不僅因為它避免了用來查找的自動“裝箱”的keys,并且它的數據結構不依賴額外的對象去?*?各個映射中查找匹配。?*??*?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.?*?*?請注意,這個容器會保持它的映射關系在一個數組的數據結構中,通過二分檢索法驅查找key。(這里我們終于知道,為何這個工具類中,?*?提供的添加映射關系的操作中,key的類型必須是integer。因為二分檢索法,將從中間“切開”,integer的數據類型是實現這種檢索過程的保證。)?*??*?如果保存大量的數據,這種數據結構是不適合的,換言之,SparseArray這個工具類并不應該用于存儲大量的數據。這種情況下,它的效率?*?通常比傳統的HashMap更低,因為它的查找方法并且增加和移除操作(任意一個操作)都需要在數組中插入和刪除(兩個步驟才能實現)。?*??*?如果存儲的數據在幾百個以內,它們的性能差異并不明顯,低于50%。?*??*?(OK,那么光看Android官方的介紹我們就有初步結論了,大量的數據我們相對SparseArray會優先選擇HashMap,如果數據在幾百個這個數目,?*??那么選擇它們任意一個去實現區別不大,如果數量較少,就選擇SparseArray去實現。?其實如果我們理解了二分法,就很容易了SparseArray的?*??實現原理,以及SparseArray和HashMap它們之間的區別了。)?*??*?<p>Note?that?this?container?keeps?its?mappings?in?an?array?data?structure,?*?using?a?binary?search?to?find?keys.??The?implementation?is?not?intended?to?be?appropriate?for?*?data?structures?*?that?may?contain?large?numbers?of?items.??It?is?generally?slower?than?a?traditional?*?HashMap,?since?lookups?require?a?binary?search?and?adds?and?removes?require?inserting?*?and?deleting?entries?in?the?array.??For?containers?holding?up?to?hundreds?of?items,?*?the?performance?difference?is?not?significant,?less?than?50%.</p>?*?*???*?為了提高性能,這個容器包含了一個實現最優的方法:當移除keys后為了立刻使它的數組緊密,它會“遺留”已經被移除(標記了要刪除)的條目(entry)?。?*?所被標記的條目(entry)(還未被當作垃圾回收掉前)可以被相同的key復用,也會在垃圾回收機制當作所有要回收的條目的一員被回收,從而使存儲的數組更緊密。?*??*?(我們下面看源碼就會發現remove()方法其實是調用delete()方法的。印證了上面這句話所說的這種優化方法。?*?因為這樣,能在每次移除元素后一直保持數組的數據結構是緊密不松散的。)?*??*?垃圾回收的機制會在這些情況執行:數組需要擴充,或者映射表的大小被恢復,或者條目值被重新檢索后恢復的時候。?*???*?<p>To?help?with?performance,?the?container?includes?an?optimization?when?removing?*?keys:?instead?of?compacting?its?array?immediately,?it?leaves?the?removed?entry?marked?*?as?deleted.??The?entry?can?then?be?re-used?for?the?same?key,?or?compacted?later?in?*?a?single?garbage?collection?step?of?all?removed?entries.??This?garbage?collection?will?*?need?to?be?performed?at?any?time?the?array?needs?to?be?grown?or?the?the?map?size?or?*?entry?values?are?retrieved.</p>?*?*?當調用keyAt(int)去獲取某個位置的key的鍵的值,或者調用valueAt(int)去獲取某個位置的值時,可能是通過迭代容器中的元素?*?去實現的。?*?*?<p>It?is?possible?to?iterate?over?the?items?in?this?container?using?*?{@link?#keyAt(int)}?and?{@link?#valueAt(int)}.?Iterating?over?the?keys?using?*?<code>keyAt(int)</code>?with?ascending?values?of?the?index?will?return?the?*?keys?in?ascending?order,?or?the?values?corresponding?to?the?keys?in?ascending?*?order?in?the?case?of?<code>valueAt(int)<code>.</p>?*/?? public?class?SparseArray<E>?implements?Cloneable?{??//...?? }???
至于完整的源碼就不貼出來了,因為不多,大家可以自行看看。
?
這里總結下幾個重要的點:
1,SparseArray的原理是二分檢索法,也因此key的類型都是整型。
2,(HashMap和SparseArray比較)當存儲大量數據(起碼上千個)的時候,優先選擇HashMap。如果只有幾百個,用哪個區別不大。如果數量不多,優先選擇SparseArray。
3,SparseArray有自己的垃圾回收機制。(當數量不是很多的時候,這個不必關心。)
接著將里面的主要方法列出來:
private?int?index?=?1;??private?String?value?=?"value";??public?void?testSparseArray()??{??//創建一個SparseArray對象??SparseArray<String>?sparseArray?=?new?SparseArray<String>();??//向sparseArray存入元素value,key為index??sparseArray.put(index,?value);??//這個方法本質也是利用put(key,?value)去存入數據??sparseArray.append(index,?value);??sparseArray.indexOfKey(index);??//查找value所在的位置,如果不存在,則返回-1??sparseArray.indexOfValue(value);??//更新某個key的值??sparseArray.setValueAt(index,?value);??//獲取index所對應的值,沒有則返回null??sparseArray.get(index);??//獲取index所對應的值,沒有則返回自定義的默認值"default-value"??sparseArray.get(index,"default-value");??//刪除index對應的元素??sparseArray.delete(index);??//移除,本質也是調用delete(int)方法??sparseArray.remove(index);??//清空所有數據??sparseArray.clear();??}??SparseBooleanArray和SparseIntArray
SparseBooleanArray和SparseIntArray,其實看名字也知道,它們跟SparseArray極其類似,只是存儲類型加以限制了。SparseBooleanArray只能存儲boolean值,而SparseIntArray只能存儲integer類型的值。它們也同樣實現了Cloneable接口,可以直接調用clone方法,也同樣是以二分法為依據。而其他的主要方法也是一樣的。下面以SparseBooleanArray為簡單例子寫出主要的方法,從方法看出,兩者和SparseArray的確是灰常類似的。SparseIntArray的代碼就不再貼出來了,因為都一樣的。我們在使用的過程中舉一反三,會用一個,其他2個也就會用了呢。
public?void?testSparseBooleanArray()??{??//??????SparseBooleanArray?sparseBooleanArray?=?new?SparseBooleanArray();??//這種創建方式可以設置容器的大小??SparseBooleanArray?sparseBooleanArray?=?new?SparseBooleanArray(5);??//存入數據,同樣有兩種方法??sparseBooleanArray.put(int,?boolean);??sparseBooleanArray.append(int,?boolean);??//根據key獲取對應的boolean值,沒有則返回false??sparseBooleanArray.get(key);??//跟上面類似,valueIfKeyNotFound是自定義的假設不存在則返回的默認值??sparseBooleanArray.get(key,?valueIfKeyNotFound);??//獲取第5個位置的鍵值??sparseBooleanArray.keyAt(5);??//獲取第5個元素的值??sparseBooleanArray.valueAt(5);??//刪除某個key的元素??sparseBooleanArray.delete(key);??//清除所有??sparseBooleanArray.clear();??}??那么SparseBooleanArray和SparseIntArray也和SparseArray一樣,存儲不是太多的數據,它們都是作為比HashMap更好的選擇。但數據是死的,功能也是死的,實現方式是靈活的。條條大路通羅馬。我們不能一概而論說誰好誰差,放在具體的場景,才能選擇更高效也更合乎成本的實現方式。
轉載于:https://www.cnblogs.com/ganchuanpu/p/7696251.html
總結
以上是生活随笔為你收集整理的SparseArray代替HashMap的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 结构体之 JavaStruct
- 下一篇: 【190111】VC+Access工程信