ArrayMap源码分析

  1. 成员变量说明
    1. private static final boolean CONCURRENT_MODIFICATION_EXCEPTIONS = true;  //多线程操作判断,值为 true 时一些关键地方会进行判断,如果有多线程操作破坏数据的情况会报错
    2. private static final int BASE_SIZE = 4;//缓存的最小单位,为4个字节
    3. private static final int CACHE_SIZE = 10;//缓存数量
    4. static final int[] EMPTY_IMMUTABLE_INTS = new int[0];//初始化时分配的一个空的hash数组
    5. public static final ArrayMap EMPTY = new ArrayMap<>(-1);
    6. int[] mHashes;//hash数组
    7. Object[] mArray;//key-value数组
  2. ArrayMa采用数组存储Hash和Object数据,利用缓存机制复用已分配的空间,使用二分查找法进行匹配查找
  3. 存储逻辑如下
    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
  4. 分配空间和释放空间分别使用allocArrays 和 freeArrays 两个函数
    1. 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);
    2. 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异常

  5. 遗留问题
    1. ArrayMap本身是非线程安全,为什么还要做synchronized操作,这样单线程使用会耗时,联系里面的很多多线程判断,难道是历史遗留问题?
    2. 缓存里面的mBaseCacheSize 作用,多个缓存实际只有一个能用,多个缓存的意义是什么?
  6. 参考文章
    

    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

     

     

     

     

 

你可能感兴趣的:(android)