在Andorid的源碼和第三方庫中,偶爾能看到該類,咱們先來看一下官方文檔的說明以下:數組
SparseArray 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.數據結構
上面的意思是SparseArray 用來替代HashMap Int到Object的這種關係。 它設計的目的是爲了比HashMap更加節省內存,這是由於:app
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%.less
上面說了,SparseArray經過二分查找鍵值,這種實現方式不太適合太多的item。一般狀況他是比HashMap慢的,可是若是容器只有數百的item,這個性能損失不過重要,不超過50%。性能
實際在Android環境中,咱們的鍵值也不多有超過上千的,因此SparseArray咱們能在項目中用到的地方仍是很多,若是在Android Stuido出現一個黃色警告叫你替換的話,你就能夠考慮替換了。由於對於Android 來講,內存每每比較重要一點。測試
下面咱們分析SparseArray的實現方式,從字面意思上翻譯過來就是稀疏數組,首先咱們打開源碼看一下SparseArray的結構是怎麼樣的:ui
public class SparseArray<E> implements Cloneable { private static final Object DELETED = new Object(); private boolean mGarbage = false; private int[] mKeys; private Object[] mValues; private int mSize; ........我是省略的代碼喲.......... }
看的出代碼中有一個 int[] mKeys 數組, 和一個 Object[] mValues 數組,這兩個就是存放鍵值和對象的地方,mSize是咱們存入了多少鍵值對。下面咱們將要用一個很是土的辦法來看看這個結構是怎麼樣的,咱們寫一段測試代碼以下,斷點調試。this
1 SparseArray<Object> sparseArray = new SparseArray<>(); 2 sparseArray.put(1, "11"); 3 sparseArray.put(8, "13"); 4 sparseArray.put(4, "12"); 5 sparseArray.put(0, "30");
咱們執行第二行代碼之後:google
mKey中的結構: {1,0,0,0,0,0,0,0,0,0,0,0} 後面的0是初始化mKey的長度 mValues中的結構: {"11"}
mSize: 1spa
咱們執行第三行代碼之後:
mKey中的結構: {1,8,0,0,0,0,0,0,0,0,0,0} 後面的0是初始化mKey的長度 mValues中的結構: {"11","13"}
mSize: 2
咱們執行第三行代碼之後:
mKey中的結構: {1,4,8,0,0,0,0,0,0,0,0,0} 後面的0是初始化mKey的長度 mValues中的結構: {"11","12","13"}
mSize: 3
咱們執行第三行代碼之後:
mKey中的結構: {0,1,4,8,0,0,0,0,0,0,0,0,0} 後面的0是初始化mKey的長度 mValues中的結構: {"30","11","12","13"}
mSize: 4
從以上的結構中咱們能夠判斷出,若是咱們想查找一個鍵值爲4的key,那麼首先第一步找到key對應在mKey數組中的index,那麼對應的value就在對應mValues中的index位置。再仔細觀察上面的mKey中的結構,你會發現mKey中的數字是遞增的,那麼這保證了咱們就能夠經過二分查找去找到某一個值在mkey中的位置。ok這個結構分析完畢以後,咱們接下來看看,增,刪,查,找功能。
/** * Adds a mapping from the specified key to the specified value, * replacing the previous mapping from the specified key if there * was one. */ public void put(int key, E value) { //經過二分查找鍵值 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); //若是找到了 就直接替換 if (i >= 0) { mValues[i] = value; } else { //若是沒有找到,這個返回的值就是沒有找到的mid+1 那麼再取反 正好是當前mKey的存有值 //的下一個值,只能說好巧妙~~ i = ~i; if (i < mSize && mValues[i] == DELETED) { mKeys[i] = key; mValues[i] = value; return; } if (mGarbage && mSize >= mKeys.length) { gc(); // Search again because indices may have changed. i = ~ContainerHelpers.binarySearch(mKeys, mSize, key); } //GrowingArrayUtils 這個源碼看不到,google下源碼,就是他會動態增長數組的size, //經過System.arraycopy 實現的,具體本身去google咯 mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } }
/** * Removes the mapping from the specified key, if there was any. */ public void delete(int key) { //仍是二分查找 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); //找到了刪除 if (i >= 0) { if (mValues[i] != DELETED) { mValues[i] = DELETED; mGarbage = true; } } }
/** * Gets the Object mapped from the specified key, or the specified Object * if no such mapping has been made. */ @SuppressWarnings("unchecked") public E get(int key, E valueIfKeyNotFound) { //仍是二分查找 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); if (i < 0 || mValues[i] == DELETED) { return valueIfKeyNotFound; } else { return (E) mValues[i]; } }
看了以上的操做,都是經過二分查找來操做的,因此和HashMap相比, 性能仍是有所損失的,最後看看GC的操做
private void gc() { // Log.e("SparseArray", "gc start with " + mSize); int n = mSize; int o = 0; int[] keys = mKeys; Object[] values = mValues; //循環查找 找到了置空 for (int i = 0; i < n; i++) { Object val = values[i]; if (val != DELETED) { if (i != o) { keys[o] = keys[i]; values[o] = val; values[i] = null; } o++; } } mGarbage = false; mSize = o; // Log.e("SparseArray", "gc end with " + mSize); }
總結一下, 就是SpareArray 比HashMap更節約內存,可是性能不如HashMap,在Android系統這內存似金的年代,咱們仍是應該想盡各類辦法去節約內存的。 SpareArray還有一個兄弟 叫LongSparsArray 實現原理也是同樣的。