ArrayMap 数组复用问题

38 阅读2分钟

问题描述:

  • 项目中存在大量使用 Hashmap 的情况,存在如下问题

    • HashMap 内部创建的数组和其他对象无法复用,带来内存回收的压力
    • 代码规范问题,有的RD会在构造方法中带 capacity 有的不会,这样导致问题复杂度提升
  • ArrayMap 实现了数组的缓存,针对移动端开发场景更适合

    • 需要明确不带 capacity 时的实现机制

问题跟踪:

1、不带 capacity 的构造参数

/**
 * Create a new empty ArrayMap.  The default capacity of an array map is 0, and
 * will grow once items are added to it.
 */
public ArrayMap() {
    this(0, false);
}

public ArrayMap(int capacity, boolean identityHashCode) {
    mIdentityHashCode = identityHashCode;
    // If this is immutable, use the sentinal EMPTY_IMMUTABLE_IN
    // instance instead of the usual EmptyArray.INT. The referen
    // is checked later to see if the array is allowed to grow.
    if (capacity < 0) {
        mHashes = EMPTY_IMMUTABLE_INTS;
        mArray = EmptyArray.OBJECT;
    } else if (capacity == 0) {
        mHashes = EmptyArray.INT;
        mArray = EmptyArray.OBJECT;
    } else {
        allocArrays(capacity);
    }
    mSize = 0;
}

capacity 为 0 时 mHashes 和 mArray 两个数组对应的初始值为 EmptyArray 中的空数组

public final class EmptyArray {
    ...
    public static final int[] INT = new int[0];
    public static final Object[] OBJECT = new Object[0];
    ...
}

2、map.put(key, value) 实现

public V put(K key, V value) {
    final int osize = mSize;
    final int hash;
    int index;
    if (key == null) {
        hash = 0;
        index = indexOfNull();
    } else {
        hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
        // 这里因为 mSize 为 0 ,所以返回的值为 ~0 = -1
        index = indexOf(key, hash);
    }
    if (index >= 0) {
        index = (index<<1) + 1;
        final V old = (V)mArray[index];
        mArray[index] = value;
        return old;
    }
    index = ~index;
    if (osize >= mHashes.length) {
    	// 扩容策略
        final int n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1))
                : (osize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
        if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);
        final int[] ohashes = mHashes;
        final Object[] oarray = mArray;
        allocArrays(n);
        if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
            throw new ConcurrentModificationException();
        }
        if (mHashes.length > 0) {
            if (DEBUG) Log.d(TAG, "put: copy 0-" + osize + " to 0");
            System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
            System.arraycopy(oarray, 0, mArray, 0, oarray.length);
        }
        freeArrays(ohashes, oarray, osize);
    }
    if (index < osize) {
        if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (osize-index)
                + " to " + (index+1));
        System.arraycopy(mHashes, index, mHashes, index + 1, osize - index);
        System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
    }
    if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
        if (osize != mSize || index >= mHashes.length) {
            throw new ConcurrentModificationException();
        }
    }
    mHashes[index] = hash;
    mArray[index<<1] = key;
    mArray[(index<<1)+1] = value;
    mSize++;
    return null;
}

这里不再贴出缓存相关的代码

问题结论:

  • ArrayMap 实现了数组对象的复用,能减少垃圾回收的压力
  • ArrayMap 设计时考虑了并发的安全性问题,是因为设计时就考虑了更广泛的场景