概述
ArrayMap 是 Android 的 API,它和 Java 的 HashMap 相比,虽然在查找效率上不如 HashMap(HashMap 查找的时间复杂度是 O(1), ArrayMap 查找的时间复杂度是 O(logn)),但是 ArrayMap 的空间消耗更小,它内部使用数组存储 hash 和键值对,不用花费多余的指针维护链表或树结构,扩容的时候只扩容 1.5 倍,并且元素小于一定量后还会收缩数组来回收空间。所以在数据量不大并且需要节省空间的时候可以考虑 ArrayMap。
下面就从源码的角度看下 ArrayMap 的实现原理,主要看它的增删查方法。
主要属性
1 | final boolean mIdentityHashCode; // 是否利用 System.identityHashCode(key) 获取唯一 hashCode |
构造方法
ArrayMap()1
2
3public ArrayMap() {
this(0, false);
}
默认构造方法,初始容量为 0,第一次插入元素时再扩容
ArrayMap(int)1
2
3public ArrayMap(int capacity) {
this(capacity, false);
}
指定初始容量
ArrayMap(int, boolean)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public ArrayMap(int capacity, boolean identityHashCode) {
mIdentityHashCode = identityHashCode;
if (capacity < 0) { // 容量小于 0,创建一个不可变的 ArrayMap
mHashes = EMPTY_IMMUTABLE_INTS;
mArray = EmptyArray.OBJECT;
} else if (capacity == 0) { // 容量等于 0,创建一个空 ArrayMap
mHashes = EmptyArray.INT;
mArray = EmptyArray.OBJECT;
} else {
// 容量大于 0,根据指定容量初始化 mHashes 和 mArray 数组
// 如果容量是 4 或 8 的话,会查看之前是否有缓存的数组,有就使用缓存
allocArrays(capacity);
}
mSize = 0;
}
指定初始容量和 mIdentityHashCode
put
1 | public V put(K key, V value) { |
首先得到 key 的 hash 值,null 的 hash 值为 0,对于非 null 的 key,默认情况下使用 key 的 hashCode() 作为它的 hash 值,如果在创建 ArrayMap 时指定 mIdentityHashCode 为 true,则 key 的 hash 值为 System.identityHashCode(key)。
然后使用二分法在 mHashes 数组查找 key 的 hash 所在位置索引 index。如果 index >= 0,说明存在相同元素,更新 value,返回旧的 value。否则就要根据 index 将新元素插入到合适位置。
在插入前,判断是否要扩容,当键值对数量大于等于 mHashes 数组的长度时,进行扩容。扩容过程和 ArrayList 相似,得到新数组长度,创建新数组,并将旧数组的元素复制过去。新数组长度和旧数组长度有关:
如果旧数组长度小于 4,那么新数组长度为 4;如果旧数组长度大于等于 4 并且小于 8,那么新数组长度为 8;如果旧数组长度大于等于 8,新数组长度是原来的 1.5 倍。
插入元素的时候,如果不是插入到最后,那么需要先将位于插入位置及其后面的元素后移,腾出位置,然后将 hash 和键值对插入到 mHashes 和 mArray 的相应位置。
得到 index 使用的是 indexOfNull()
和 indexOf
方法:
indexOfNull()
1 | int indexOfNull() { |
indexOf
1 | int indexOf(Object key, int hash) { |
remove
1 | public V remove(Object key) { |
先找到 key 的 index,然后调用 removeAt 方法
1 | public V removeAt(int index) { |
在删除元素后,如果发现当前容量大于 8,并且剩余的键值对数量小于容量的 1/3 时,将收缩数组,如果键值对数量小于等于 8,那么新数组长度为 8;如果键值对数量大于 8,那么新数组长度为键值对数量的 1.5 倍。
get
1 | public V get(Object key) { |
它的 get 方法比较简单,得到 key 的 hash 在 mHashes 的 index 后,如果 index 大于等于 0,通过索引可以直接从 mArray 数组得到 value。如果 index 小于 0,说明不存在该元素,返回 null。