一個數組存的是key的hash值,一個存在的key value。結構以下。java
嗯,如標題所言是非線程安全的,多個線程共享ArrayMap的時候就有數據不一致的問題。算法
提及來很簡單,就是往ArrayMap中存鍵值對,若是要存的這個key在HashMap中已經存在了的就用新值替換掉,若是不存在key就連值帶鍵都存在裏面。 大致的過程是這樣的。設計模式
當ArrayMap的容量已滿的時候就要使用擴容機制了,具體怎麼擴呢源碼中找答案。數組
if (osize >= mHashes.length) {
final int n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1))
: (osize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
final int[] ohashes = mHashes;
final Object[] oarray = mArray;
allocArrays(n);
...
複製代碼
一波三目運算道出真理:緩存
沒什麼好說的 什麼底層源碼 什麼設計模式 一梭子代碼安全
@Override
public V get(Object key) {
final int index = indexOfKey(key);
return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}
複製代碼
刪除操做,本質就是對數組進行復制操做,正常狀況下,bash
當ArrayMap中的元素數小於容量的1/3的時候啓動縮容模式,縮容到當期數量(不是容量)的1.5倍,若是數量小於8直接擴容到8。ide
if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
final int n = osize > (BASE_SIZE*2) ? (osize + (osize>>1)) : (BASE_SIZE*2);
....
}
複製代碼
static Object[] mBaseCache;//容量爲4,ArrayMap緩存鏈表
static int mBaseCacheSize;//容量爲4的緩存數量
static Object[] mTwiceBaseCache;//容量爲8的Array緩存鏈表
static int mTwiceBaseCacheSize;//容量爲8的緩存數量
複製代碼
private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
//當前hashs數據的長度爲8
if (hashes.length == (BASE_SIZE*2)) {
...
} else if (hashes.length == BASE_SIZE) {
synchronized (ArrayMap.class) {
if (mBaseCacheSize < CACHE_SIZE) {
array[0] = mBaseCache;//把array的第一個元素指向當前的mBaseCache
array[1] = hashes;//把array的第二個元素指向hash數組
for (int i=(size<<1)-1; i>=2; i--) {//把第二個之後的元素都置空
array[i] = null;
}
mBaseCache = array;//mBaseCache執行array
mBaseCacheSize++;//數字加1
}
}
}
}
複製代碼
看註釋,應該就明白了,若是還不明白看看下面的這張圖。ui
關注下,freeArrays()方法中的synchronized關鍵字,不是說好的線程不安全嗎,怎麼還有出現synchronized呢google
線程安全問題的本質是,可變數據在多條線程中共享。 mBaseCache和mTwiceBaseCache都是靜態的,也就是說會存在他們多個ArrayMap對象訪問他們的狀況。反過來思考,一個ArrayMap對象,在單一線程中建立使用,擴容縮容的時候若是沒有加同步塊會出現線程安全問題。
就緩存兩種容量的map,容量爲4和容量爲8的
private void allocArrays(final int size) {
if (size == (BASE_SIZE*2)) {
//當要分配數組的容量爲8時
...
} else if (size == BASE_SIZE) {
//當分配數組的容量爲4時
synchronized (ArrayMap.class) {
if (mBaseCache != null) {
final Object[] array = mBaseCache;
mArray = array;//mArray指向緩存鏈表中第一個數組
mBaseCache = (Object[])array[0];//mBaseCache指向下一個數組
mHashes = (int[])array[1];//mHashes數組指向arry的第一個元素
array[0] = array[1] = null;//置空
mBaseCacheSize--;
return;
}
}
}
mHashes = new int[size];
mArray = new Object[size<<1];
}
複製代碼
google官方建議當數據量小於1000的時候,推薦使用ArrayMap,但於1000使用HashMap。ArrayMap查詢效率爲O(logN),刪除和添加元素的時候都要移動數組效率較低 。HashMap的查詢更改速度爲O(1)。可是ArrayMap優點就是省內存!
除了ArrayMap以外還有SpareArray,原理和ArrayMap差很少,但他的key爲int類型,避免了基本數據類型的拆箱裝箱帶來的效率問題。當肯定value的值爲int/long/boolean的時候就可使用SparseIntArray/SparseLongArray/SpareseBoolean。
補充一個二分查找算法
public static int binarySearch(int[] array,int value){
if (array == null){
throw new IllegalArgumetsException("不能爲空");
}
int lo = 0;
int li = array.length - 1;
while(lo<=li){
int index = (lo+li)/2;
int midValue = array[index];
if(value>midValue){
li= index+1;
}else if(value<midValue){
lo = index-1;
}else{
return index;
}
}
return ~lo;
}
複製代碼