- 成员变量说明
-
private static final boolean CONCURRENT_MODIFICATION_EXCEPTIONS = true; //多线程操作判断,值为 true 时一些关键地方会进行判断,如果有多线程操作破坏数据的情况会报错
-
private static final int BASE_SIZE = 4;//缓存的最小单位,为4个字节
-
private static final int CACHE_SIZE = 10;//缓存数量
-
static final int[] EMPTY_IMMUTABLE_INTS = new int[0];//初始化时分配的一个空的hash数组
-
public static final ArrayMap EMPTY = new ArrayMap<>(-1);
-
int[] mHashes;//hash数组
-
Object[] mArray;//key-value数组
-
- ArrayMa采用数组存储Hash和Object数据,利用缓存机制复用已分配的空间,使用二分查找法进行匹配查找
- 存储逻辑如下
526 mHashes[index] = hash; 527 mArray[index<<1] = key; 528 mArray[(index<<1)+1] = value; 529 mSize++;
其中mHashes用俩存储hash,并与mArray里面的数据相对应,mArray存储key和value,对于一个数据A,hash存储在mHahses[index] = HashA,key和value分别存储在mHahses[index<<1] = KeyA以及mHahses[[index<<1+1] = ValueA - 分配空间和释放空间分别使用allocArrays 和 freeArrays 两个函数
-
put中分配空间逻辑如下,根据osize 及mSize 大小来判断需要分配的空间,会适当多分配一些避免频繁调用allocArray
492 finalint n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1)) 493 : (osize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE); 494 495 if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n); 496 497 finalint[] ohashes = mHashes; 498 finalObject[] oarray = mArray; 499 allocArrays(n);
-
allocArrays 和 freeArrays 两个函数源码如下
190 private void allocArrays(final int size) { 191 if (mHashes == EMPTY_IMMUTABLE_INTS) { 192 throw new UnsupportedOperationException("ArrayMap is immutable"); 193 } 194 if (size == (BASE_SIZE*2)) { 195 synchronized (ArrayMap.class) { 196 if (mTwiceBaseCache != null) { 197 final Object[] array = mTwiceBaseCache; 198 mArray = array; 199 mTwiceBaseCache = (Object[])array[0]; 200 mHashes = (int[])array[1]; 201 array[0] = array[1] = null; 202 mTwiceBaseCacheSize--; 203 if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes 204 + " now have " + mTwiceBaseCacheSize + " entries"); 205 return; 206 } 207 } 208 } else if (size == BASE_SIZE) { 209 synchronized (ArrayMap.class) { 210 if (mBaseCache != null) { 211 final Object[] array = mBaseCache; 212 mArray = array; 213 mBaseCache = (Object[])array[0]; 214 mHashes = (int[])array[1]; 215 array[0] = array[1] = null; 216 mBaseCacheSize--; 217 if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes 218 + " now have " + mBaseCacheSize + " entries"); 219 return; 220 } 221 } 222 } 223 224 mHashes = new int[size]; 225 mArray = new Object[size<<1]; 226 } 227 228 private static void freeArrays(final int[] hashes, final Object[] array, final int size) { 229 if (hashes.length == (BASE_SIZE*2)) { 230 synchronized (ArrayMap.class) { 231 if (mTwiceBaseCacheSize < CACHE_SIZE) { 232 array[0] = mTwiceBaseCache; 233 array[1] = hashes; 234 for (int i=(size<<1)-1; i>=2; i--) { 235 array[i] = null; 236 } 237 mTwiceBaseCache = array; 238 mTwiceBaseCacheSize++; 239 if (DEBUG) Log.d(TAG, "Storing 2x cache " + array 240 + " now have " + mTwiceBaseCacheSize + " entries"); 241 } 242 } 243 } else if (hashes.length == BASE_SIZE) { 244 synchronized (ArrayMap.class) { 245 if (mBaseCacheSize < CACHE_SIZE) { 246 array[0] = mBaseCache; 247 array[1] = hashes; 248 for (int i=(size<<1)-1; i>=2; i--) { 249 array[i] = null; 250 } 251 mBaseCache = array; 252 mBaseCacheSize++; 253 if (DEBUG) Log.d(TAG, "Storing 1x cache " + array 254 + " now have " + mBaseCacheSize + " entries"); 255 } 256 } 257 } 258 }
虽然ArrayMap本身是线程不安全的,但是这里还是用到了synchronized (ArrayMap.class) ,这里让人费解,这里也有一个小bug存在,前面的博客里面有详细描述,就是脏数据问题,简单说就是虽然这两个函数都做了 synchronized (ArrayMap.class)处理看起来不会产生多线程问题,但是freeArrays的array被赋值为 array[0] = mBaseCache;之后外面可能会修改这个值,导致allocArrays中mBaseCache = (Object[])array[0];时array[0]可能存的不一定是之前的mBaseCache地址,会报CastException异常
-
-
遗留问题
-
ArrayMap本身是非线程安全,为什么还要做synchronized操作,这样单线程使用会耗时,联系里面的很多多线程判断,难道是历史遗留问题?
-
缓存里面的mBaseCacheSize 作用,多个缓存实际只有一个能用,多个缓存的意义是什么?
-
-
参考文章
ArrayMap完全剖析:https://www.jianshu.com/p/1a14fc87b935
ArrayMap完全剖析之线程安全:https://www.jianshu.com/p/02890898ee68
ArrayMap源码:http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/util/ArrayMap.java