當我們在安卓開發過程中需要使用到鍵值對對映的時候,自然而然的會想到使用hashmap
,但是當我們指定key
的泛型為int
型時,ide卻會提示我們使用sparsearray
來替換hashmap
,這是為什麼呢?難道sparsearray相較於hashmap有什麼優勢嗎?帶著疑問我們去探尋一下sparsearray
的原始碼。
sparsearray
類位於android.util這個包下,官方對sparsearray
簡介如下:
翻譯過來就是:sparsearray
用於將整形對映到物件,對於這樣的鍵值對對映,它有著比hashmap
更高的記憶體效率,因為其避免了整形自動拆裝箱的過程,並且不需要額外的entry
結構。
可以根據圖歸納出以下幾點:
還有幾個比較關鍵的點圖中沒有展示:
sparsearray
內部的屬性很少,如下:
private static final object deleted = new object();
// 用來標記當前內部是否有 key->deleted 這樣的髒資料
private boolean mgarbage = false;
private int mkeys; // 用於存放keys的陣列
private object mvalues; // 用於存放values陣列
private int msize; // 鍵值對的數量
複製**
public sparsearray(int initialcapacity) else
msize = 0;
} public sparsearray
() 複製**
sparsearray
有兩個建構函式,無參建構函式內部呼叫了帶初始容量的建構函式,該建構函式內部進行了key,value
陣列的初始化。我們得知如果使用無參建構函式,key,value
陣列的初始容量均為10,比較簡單. 接下來看一看關鍵的put
方法。
public void put(int key, e value) else
// 當陣列中存在key->delete鍵值對,並且陣列已經不能容下新的鍵值對時,進行gc()操作,清除無意義鍵值對
if (mgarbage && msize >= mkeys.length)
// 在i處利用工具類插入 key和value,從growingarrayutils名字可以看出,當key和value陣列容量不足時,會先對陣列擴容,在插入。
mkeys = growingarrayutils.insert(mkeys, msize, i, key);
mvalues = growingarrayutils.insert(mvalues, msize, i, value);
msize++; // 插入成功 重新整理容量}}
// ------------------binarysearch(int array, int size, int value)-----
static int binarysearch(int array, int size, int value) else
if (midval > value) else
}// 到了這一步,說明未查到key。這裡記住乙個規律,當二分查詢在陣列中未找到某個值,則此時lo的值為最適合插入陣列的下標,這裡進行取反然後再返回,這樣方便讓外層根據正負判斷是否找到對應key的下標。
return ~lo; // value not present
}//---------------- gc() ---------------------
private void gc
() o++;}}
mgarbage = false; // 陣列中不再有key->delete 鍵值對
msize = o; // 恢復正確的鍵值對容量
// log.e("sparsearray", "gc end with " + msize);
}複製**
put
方法是sparsearray
中比較難的方法了,流程如下:
比較關鍵的地方:
下面看get()方法
public e get(int key)
public e get(int key, e valueifkeynotfound) else
}複製**
get
方法比較簡單,第二個get
方法類似於hashmap
中的getordefault()
方法,找不到對應的value
時會返回乙個傳入的預設值。 下面看我們的delete
方法。
public void delete(int key) }}
// 內部呼叫了delete方法
public void remove(int key)
複製**
我們看到,呼叫delete
或者remove
方法刪除資料時並不是真正的刪除了資料,只是將key->value
變成了key->delete
的形式,看過put
方法之後,不難發現,在某種情況下,比如這種:
sparsearray sparsearray = new sparsearray();
sparsearray.put(5,obj1); // 存入5 ->obj1
sparsearray.delete(5); // 刪除 key 為 5的鍵值對,根據刪除機制,此時鍵值對情況為 5 ->delete
sparsearray.put(1,obj2); // 此時,欲存入1->obj2 鍵值對,根據二分查詢,應該插入下標為0 ,此時mvalues[0] = delete,因此,直接替換mkeys[0]為1,mvalues[0]為obj2,鍵值對情況更新為 1->obj2。
複製**
可以發現,sparsearray
的刪除機制在某些情況下能免去一次刪除操作和一次插入操作,提公升了效率。
public int size
() return msize;
}複製**
因為可能存在髒資料,所以return
之前進行了gc()
判斷。
public int indexofvalue(e value)
for (int i = 0; i < msize; i++)
}return -1;
}public int indexofvaluebyvalue(e value)
for (int i = 0; i < msize; i++)
} else }}
return -1;
}複製**
兩個方法的作用都是獲取某個value
在values
陣列的下標,但是有一點小區別。indexofvalue(e value)
使用 「==」 來進行條件判斷,表示查詢的值和容器中的value
為同一物件時,才返回下標,而indexofvaluebyvalue(e value)
則沒有那麼嚴格,使用equals方法
進行條件判斷。需要使用者根據不同的需求呼叫對應的方法。
還有一些零零閃閃的方法就不一一列出來了,知道了原理,都相對簡單。
好了,以上就是sparsearray
大致的原始碼解析了,比起hashmap
,sparsearray
避免了自動拆裝箱過程,內部也僅使用了兩個陣列來儲存鍵值對,比較輕巧簡單,對記憶體更加友好,所以在專案中,如果是遇到key
的型別為int
的情況,應該盡量使用sparsearray
來替代hashmap
。但是值得注意的是,sparsearray
使用二分查詢來獲取key
在陣列中的下標,其時間複雜度為o(log(n)),當存放的資料量達到千量級以上,訪問耗時會比較明顯,此時,使用時間複雜度更低的hashmap
會是更好的選擇,不過這種情況相對較少。
好了,以上就是我對sparsearray
的理解和解讀,如果錯誤,歡迎指正。好久沒寫部落格啦,希望自己以後能陸陸續續的分享一些東西出來。
azkaban web server原始碼解析
azkaban主要用於hadoop相關job任務的排程,但也可以應用任何需要排程管理的任務,可以完全代替crontab。azkaban主要分為web server 任務上傳,管理,排程 executor server 接受web server的排程指令,進行任務執行 1.資料表 projects 工...
JDK LinkedHashMap原始碼解析
今天來分析一下jdk linkedhashmap的源 public class linkedhashmapextends hashmapimplements map可以看到,linkedhashmap繼承自hashmap,並且也實現了map介面,所以linkedhashmap沿用了hashmap的大...
Redux原始碼createStore解讀常用方法
const store createstore reducer,preloadedstate enhancer 直接返回當前currentstate,獲取state值,return state 我覺得應該深轉殖乙個新的物件返回,不然有可能會被外部修改 function getstate consol...