文章目录
- [Android ArrayMap源码分析](#Android ArrayMap源码分析)
Android ArrayMap源码分析
概述
- ArrayMap 是 Android 提供的一种轻量级的 Map 实现,在一定程度上替代 HashMap 的使用。
- 内部使用数组存储数据:
- hash数组:存储 key 的哈希值,升序排列
- Array数组:存储键和值,交替存放:
[key1, value1, key2, value2 ,...]
- 键是有序的,使用二分查找法。
- 支持空键和空值。
- 线程不安全。
源码分析
基本属性
java
public final class ArrayMap<K, V> implements Map<K, V> {
// 存储key的哈希值,升序排列
int[] mHashes;
// 存储key和value,交替存放:[key1,value1,key2,value2,....]
Object[] mArray;
// 元素数量
int mSize;
}
构造函数
java
public ArrayMap() {
this(0, false);
}
public ArrayMap(int capacity) {
this(capacity, false);
}
public ArrayMap(int capacity, boolean identityHashCode) {
mIdentityHashCode = identityHashCode;
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;
}
public ArrayMap(ArrayMap<K, V> map) {
this();
if (map != null) {
putAll(map);
}
}
put()
使用二分查找法获取位置,如果找到位置则替换旧值,如果没有找到则插入新值,插入操作是需要移动数组。
扩容策略:小于 4 → 扩到 4;4~8 → 扩到 8;大于 8 → 扩 1.5 倍。
java
@Override
public V put(K key, V value) {
final int osize = mSize;
final int hash;
int index;
if (key == null) {
// 如果key为null则查找null键位置
hash = 0;
index = indexOfNull();
} else {
// 如果key不为null,则获取key的hash值,使用二分查找法获取位置
hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
index = indexOf(key, hash);
}
if (index >= 0) {
// 如果index存在,则覆盖旧值
index = (index<<1) + 1;
final V old = (V)mArray[index];
mArray[index] = value;
return old;
}
// key不存在,插入新值
index = ~index;
if (osize >= mHashes.length) {
final int n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1))
: (osize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
// 保存旧数组引用,用于拷贝数据
final int[] ohashes = mHashes;
final Object[] oarray = mArray;
// 分配新数组,扩容操作
allocArrays(n);
// 将旧数据拷贝到新数组中
if (mHashes.length > 0) {
System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
System.arraycopy(oarray, 0, mArray, 0, oarray.length);
}
// 释放旧数组
freeArrays(ohashes, oarray, osize);
}
// 插入新数据操作
if (index < osize) {
System.arraycopy(mHashes, index, mHashes, index + 1, osize - index);
System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
}
mHashes[index] = hash;
mArray[index<<1] = key;
mArray[(index<<1)+1] = value;
// 更新元素数量
mSize++;
// 插入新值返回null
return null;
}
indexOf()
java
int indexOf(Object key, int hash) {
final int N = mSize;
if (N == 0) {
return ~0;
}
// 通过二分查找法获取位置
int index = binarySearchHashes(mHashes, N, hash);
// 未找到
if (index < 0) {
return index;
}
// hash值相同且key值也相同
if (key.equals(mArray[index<<1])) {
return index;
}
// 哈希冲突
int end;
// 向后搜索
for (end = index + 1; end < N && mHashes[end] == hash; end++) {
// << 1是位运算,相当于乘以2
// 比较key值是否相等
if (key.equals(mArray[end << 1])) return end;
}
// 向前搜索
for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
if (key.equals(mArray[i << 1])) return i;
}
return ~end;
}
get()
java
public V get(Object key) {
// 二分查找法获取位置
final int index = indexOfKey(key);
// 通过位置获取value值
return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}
ArrayMap 对比 HashMap、SparseArray
| ArrayMap | HashMap | SparseArray | |
|---|---|---|---|
| 平台 | Android | Java | Android |
| 键 | 任意类型 | 任意类型 | 必须int |
| 值 | 任意类型 | 任意类型 | 任意类型 |
| 是否支持null键 | 是 | 是 | 否 |
| 是否支持null值 | 是 | 是 | 是 |
| 数据结构 | int[] mHashes Object[] mArray | 数组+链表/红黑树 | int[] mKeys Object[] mValues |
| 内存开销 | 中 | 高 | 低 |