简介
ArrayMap是一个<key,value>映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值;在数据量不大的情况下,用来代替HashMap;
int[] mHashes;是key的hash值的数组;
Object[] mArray;是key,value数组,这两个值的下标和mHashes的下标存在index<<1,(index<<1)+1的关系;
mSize为数组mHashes的插入元素个数,和mHashes的length不同;mSize<=mHashes.length;
构造函数
public ArrayMap() {
super();
}
public SimpleArrayMap() {
mHashes = ContainerHelpers.EMPTY_INTS;
mArray = ContainerHelpers.EMPTY_OBJECTS;
mSize = 0;
}
添加操作
put():插入一个元素;
put()方法主要做了几件事:1)如果mHashes数组中存在当前key的hash值,则返回对应的index,否则返回负数(负数的绝对值,就是hash在mHashes数组中的下标;并且在二分查找时,已经确定位置,已经排好序了);2)如果mHashes数组中存在当前key的hash值,则返回mArray中对应的value值;如果不存在继续往下执行;3),如果已经达到最大容量,扩容,并且copy内容;4)插入元素,并且移动原来位置;
@Override
public V put(K key, V value) {
final int osize = mSize;
final int hash;
int index;
//如果mHashes数组中存在当前key的hash值,则返回对应的index,否则返回负数;
if (key == null) {
hash = 0;
index = indexOfNull();
} else {
hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
index = indexOf(key, hash);
}
//返回mArray中对应的value值
if (index >= 0) {
index = (index<<1) + 1;
final V old = (V)mArray[index];
mArray[index] = value;
return old;
}
index = ~index;
//如果已经插入元素的个数>=mHashes.length;确定扩容规则,扩容,并且copy内容;
if (osize >= mHashes.length) {
//确定新的容量大小n
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();
}
//copy原来的内容
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;
}
indexOf()方法:如果mHashes数组中存在当前key的hash值,并且key相等,则返回对应的index,否则返回负数(负数的绝对值,就是hash在mHashes数组中的下标;并且在二分查找时,已经确定位置,已经排好序了);indexOfNull()和indexOf()方法,逻辑一致;
int indexOf(Object key, int hash) {
final int N = mSize;
如果mHashes数组没有插入元素,直接返回-1;
// Important fast case: if nothing is in here, nothing to look for.
if (N == 0) {
return ~0;
}
//二分查找当前hash在数组mHashes中的index,如果不存在,则返回负数;
int index = binarySearchHashes(mHashes, N, hash);
//如果不存在,直接返回负数;
// If the hash code wasn't found, then we have no entry for this key.
if (index < 0) {
return index;
}
//判断index按照一定规则得到newIndex在mArray中的key,判断是否相等,因为key的hash值相同的相同的情况下,equal可能不相等;
// If the key at the returned index matches, that's what we want.
if (key.equals(mArray[index<<1])) {
return index;
}
//搜索hash值相等,并且key也相等;如果存在返回对应index,不存在返回负数;分段搜素,提高效率;
// Search for a matching key after the index.
int end;
for (end = index + 1; end < N && mHashes[end] == hash; end++) {
if (key.equals(mArray[end << 1])) return end;
}
// 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的hash在mHashes中存在,但是key在mArray数组中不存在,返回负数;
// 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;
}
putAll():基本和单个元素插入一致;
@Override
public void putAll(Map<? extends K, ? extends V> map) {
//确保容量
ensureCapacity(mSize + map.size());
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
public void ensureCapacity(int minimumCapacity) {
final int osize = mSize;
if (mHashes.length < minimumCapacity) {
final int[] ohashes = mHashes;
final Object[] oarray = mArray;
allocArrays(minimumCapacity);
if (mSize > 0) {
System.arraycopy(ohashes, 0, mHashes, 0, osize);
System.arraycopy(oarray, 0, mArray, 0, osize<<1);
}
freeArrays(ohashes, oarray, osize);
}
if (CONCURRENT_MODIFICATION_EXCEPTIONS && mSize != osize) {
throw new ConcurrentModificationException();
}
}
get()方法:先在mHashes数组找到在对应key的index,如果index>=0;再从mArray中找到对应的value;否则直接返回null;
@Override
public V get(Object key) {
final int index = indexOfKey(key);
return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}
remove():找到下标index,如果index>=0 删除对应的hash值,key,value;否则返回null;
@Override
public V remove(Object key) {
//根绝key的hashCode找到key在mHashes数组中的index;
final int index = indexOfKey(key);
//index>0 说明mHashes数组中存在对应的key的hashCode
if (index >= 0) {
return removeAt(index);
}
return null;
}
public V removeAt(int index) {
//根绝index找到mArray中对应的值value
final Object old = mArray[(index << 1) + 1];
final int osize = mSize;
final int nsize;
//mHashes数组最多只有一个插入元素,直接将mHashes,mArray数组清空;
if (osize <= 1) {
// Now empty.
if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
freeArrays(mHashes, mArray, osize);
mHashes = ContainerHelpers.EMPTY_INTS;
mArray = ContainerHelpers.EMPTY_OBJECTS;
nsize = 0;
//mHashes数组的插入元素个数>1;
} else {
nsize = osize - 1;
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.
//确定数组mHashes新的容量n;
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();
}
//把原mHashes数组0-index的元素赋值到新的数组中,把原mArray数组0-2*index的元素赋值到新的数组中,从0坐标开始赋值;
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);
}
//如果index在中间,不是最后一个;将剩下的元素复制到对应的新数组中;
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 {
//删除的元素,不是最后一个,将从index+1的元素往前移;
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);
}
mArray[nsize << 1] = null;
mArray[(nsize << 1) + 1] = null;
}
}
if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
throw new ConcurrentModificationException();
}
mSize = nsize;
return (V)old;
}
问题一:不同的key,相同hashCode ArrayMap是如何处理的呢?
相同的hash值,key不相同的情况下,mHashes数组会将该hash值也存入;在获取下标时,找到对应hash值的index后,会判断equals(),相等才会返回index;如果不相等,则根据index分别向前,向后分段搜索;查找相同hash值,并且equals()也相等的index;具体业务逻辑可详看indexOf()方法;
比较
SparseArray和ArrayMap具体实现类似,主要区别在于key必须是int类型;
SparseArray,ArrayMap是一个<key,value>映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值;
ArrayMap和SparseArray一样,也会对key使用二分法进行从小到大排序,在添加、删除、查找数据的时候都是先使用二分查找法得到相应的index,然后通过index来进行添加、查找、删除等操作,所以,应用场景和SparseArray的一样,如果在数据量比较大的情况下,那么它的性能将退化至少50%。
满足下面两个条件我们可以使用ArrayMap,SparseArray代替HashMap:
- 数据量不大,最好在千级以内
- key必须为int类型,这中情况下的HashMap可以用SparseArray代替:
以上就是ArrayMap的主要内容;如有问题,请多指教,谢谢!