概述
ArrayMap是一种能够储存K ,V键值对形式的集合对象,主要有两个数组来保存数据。
一个数组保存 key的hashcode mHashes[]
另一个数组叫mArray,保存key和value,它们的关系是第一个保存key,第二个保存value,你也可以理解为偶数保存key,奇数保存value,它的size 比上一个数组大一倍。k=index,v=index+1
ArrayMap比hashmap更高效,适合数据量比较小的情况下使用,效率更高。
构造器
按照正常创建对象,不断插入数据的流程进行代码讲解。
//一个null的对象
static final int[] EMPTY_IMMUTABLE_INTS = new int[0];
//默认最小容量
private static final int BASE_SIZE = 4;
//缓存的容量
private static final int CACHE_SIZE = 10;
//key的hash数组
int[] mHashes;
//key Value的数据,偶数key 奇数obj
Object[] mArray;
// 该对象的容量
int mSize;
//是否使用System.identityHashCode(key)来获取hashcode
final boolean mIdentityHashCode;
public ArrayMap() {
this(0, false);
}
public ArrayMap(int capacity) {
// 根据容量
this(capacity, false);
}
public ArrayMap(int capacity, boolean identityHashCode) {
mIdentityHashCode = identityHashCode;
// <0 生成空的对象
if (capacity < 0) {
mHashes = EMPTY_IMMUTABLE_INTS;
mArray = EmptyArray.OBJECT;
} else if (capacity == 0) {
// 走到这里,创建两个null 数组
mHashes = EmptyArray.INT;
mArray = EmptyArray.OBJECT;
} else {
//分配空间
allocArrays(capacity);
}
mSize = 0;
}
增–插入 、修改数据
插入数据 和修改数据
public V put(K key, V value) {
//第一次插入,mSize =0
final int osize = mSize;
final int hash;
int index;
//key==null,直接value=0
if (key == null) {
hash = 0;
index = indexOfNull();
} else {
//mIdentityHashCode 默认false, 所以key.hashCode(),返回hash
hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
//去mHash数组里查找,是否有该hash存在,不存在返回负数,内部实现是二分查找,
index = indexOf(key, hash);
}
//如果大于0,之前已经存在过,直接覆盖以前内容,这里就是修改数据
if (index >= 0) {
index = (index<<1) + 1;
final V old = (V)mArray[index];
mArray[index] = value;
return old;
}
index = ~index;// 取反数,也就是即将要插入的位置。
if (osize >= mHashes.length) { //当前没有数据,size==0
final int n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1))
: (osize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
//n=4 也就是即将扩容的大小
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);
}
// 如果index在中间位置,复制下面数组,向右移位,方便插入新的数据
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();
}
}
//赋值操作,hash数组,array数组
mHashes[index] = hash;
mArray[index<<1] = key;
mArray[(index<<1)+1] = value;
mSize++;
return null;
}
// Allocations are an implementation detail.
//分配空间的细节
private void allocArrays(final int size) {
if (mHashes == EMPTY_IMMUTABLE_INTS) {
throw new UnsupportedOperationException("ArrayMap is immutable");
}
if (size == (BASE_SIZE*2)) {
synchronized (ArrayMap.class) {
if (mTwiceBaseCache != null) {
final Object[] array = mTwiceBaseCache;
mArray = array;
mTwiceBaseCache = (Object[])array[0];
mHashes = (int[])array[1];
array[0] = array[1] = null;
mTwiceBaseCacheSize--;
if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes
+ " now have " + mTwiceBaseCacheSize + " entries");
return;
}
}
//第一次,得到n=4,满足这个条件
} else if (size == BASE_SIZE) {
synchronized (ArrayMap.class) {
if (mBaseCache != null) {
final Object[] array = mBaseCache;
mArray = array;
mBaseCache = (Object[])array[0];
mHashes = (int[])array[1];
array[0] = array[1] = null;
mBaseCacheSize--;
if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes
+ " now have " + mBaseCacheSize + " entries");
return;
}
}
}
// 默认size 是0 mArray 也是0
mHashes = new int[size];
mArray = new Object[size<<1];
}
删除
通过已知的key,去删除数据
public V remove(Object key) {
//由key去找index
final int index = indexOfKey(key);
if (index >= 0) {
return removeAt(index);
}
return null;
}
public V removeAt(int index) {
if (index >= mSize && UtilConfig.sThrowExceptionForUpperArrayOutOfBounds) {
// The array might be slightly bigger than mSize, in which case, indexing won't fail.
// Check if exception should be thrown outside of the critical path.
throw new ArrayIndexOutOfBoundsException(index);
}
// 根据index 获取value
final Object old = mArray[(index << 1) + 1];
final int osize = mSize;
final int nsize;
// 如果只有一个数据,删除完后,赋值为空数组
if (osize <= 1) {
// Now empty.
if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
final int[] ohashes = mHashes;
final Object[] oarray = mArray;
mHashes = EmptyArray.INT;
mArray = EmptyArray.OBJECT;
// 回收对象
freeArrays(ohashes, oarray, osize);
nsize = 0;
} else {
nsize = osize - 1;
//mSize是存储了数据的个数,如果mSize >8 且小于hash数组的长度的三分之一
if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
// Shrunk enough to reduce size of arrays. We don't allow it to
// shrink smaller than (BASE_SIZE*2) to avoid flapping between
// that and BASE_SIZE.
final int n = osize > (BASE_SIZE*2) ? (osize + (osize>>1)) : (BASE_SIZE*2);
if (DEBUG) Log.d(TAG, "remove: shrink 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 (index > 0) {
if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
System.arraycopy(ohashes, 0, mHashes, 0, index);
System.arraycopy(oarray, 0, mArray, 0, index << 1);
}
//用复制操作去覆盖元素达到删除的目的。
if (index < nsize) {
if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + nsize
+ " to " + index);
System.arraycopy(ohashes, index + 1, mHashes, index, nsize - index);
System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,
(nsize - index) << 1);
}
} else {
//不需要压缩空间
if (index < nsize) {
if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + nsize
+ " to " + index);
//用复制操作去覆盖元素达到删除的目的。
System.arraycopy(mHashes, index + 1, mHashes, index, nsize - index);
System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,
(nsize - index) << 1);
}
// 给腾出的空间赋值null
mArray[nsize << 1] = null;
mArray[(nsize << 1) + 1] = null;
}
}
if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
throw new ConcurrentModificationException();
}
mSize = nsize;
return (V)old;
}
查询
根据key,去数组里通过二分查找的方式找到index,然后根据index 规则,获取到对象V
/**
* Retrieve a value from the array.
* @param key The key of the value to retrieve.
* @return Returns the value associated with the given key,
* or null if there is no such key.
*/
@Override
public V get(Object key) {
final int index = indexOfKey(key);
return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}
public int indexOfKey(Object key) {
return key == null ? indexOfNull()
: indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());
}
int indexOf(Object key, int hash) {
final int N = mSize;
// Important fast case: if nothing is in here, nothing to look for.
if (N == 0) {
return ~0;
}
int index = binarySearchHashes(mHashes, N, hash);
// 小于0,就是没找到,直接返回,If the hash code wasn't found, then we have no entry for this key.
if (index < 0) {
return index;
}
// 如果当前的key等于array的index*2 ,直接返回当前位置
//If the key at the returned index matches, that's what we want.
if (key.equals(mArray[index<<1])) {
return index;
}
// Search for a matching key after the index.
int end;
//如果还没有找到,从当前index的下一个位置,遍历查找,如果符合,返回下标。
for (end = index + 1; end < N && mHashes[end] == hash; end++) {
if (key.equals(mArray[end << 1])) return end;
}
//如果还没有找到,从当前index的上一个位置,遍历查找,如果符合,返回下标。
// Search for a matching key before the index.
for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
if (key.equals(mArray[i << 1])) return i;
}
// Key not found -- return negative value indicating where a
// new entry for this key should go. We use the end of the
// hash chain to reduce the number of array entries that will
// need to be copied when inserting.
//如果还没有找到,返回 负值
return ~end;
}
判断key == null ,如果是,去找key==null的index, indexOfNull()。
如果不为null,调用indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());首先判断mIdentityHashCode获取hashcode,然后通过二分查找,去找hash的下标。