SparseIntArray、SparseArray、ArrayMap 和 HashMap 的原理分析
前言
Android开发一般都使用java集合里面的HashMap HashSet等集合API, 但是由于其特殊的需求做数据处理的时候,Android为自身定义了一套自有的API来实现本身的功能和需求,在android.util 里面有这几个类: SparseArray(系列)、ArrayMap、 ArraySet等。
SparseArray的源码分析
SparseArray(稀疏数组)是Android框架提供的工具类,用于建立整数和对象之间的映射关系,效果相当于HashMap
1:SparseArray的定义的一些属性
private static final Object DELETED = new Object();
private boolean mGarbage = false;
private int[] mKeys;
private Object[] mValues;
private int mSize;
1:DELETE: 所有被删除的数据都会优先指向这个对象
2:mGarbage: 是否进行空间回收,true :立即执行移除的操作
3:mKeys: 存储Key的数组
4:mValues: 存储value的数组
5:当前元素的数量(不包含已经被标记删除的元素)
2:构造方法
public SparseArray() {
this(10);
}
/** * Creates a new SparseArray containing no mappings that will not
* require any additional memory allocation to store the specified
* number of mappings. If you supply an initial capacity of 0, the
* sparse array will be initialized with a light-weight representation
* not requiring any additional array allocations.
*/
public SparseArray(int initialCapacity) {
if (initialCapacity == 0) {
mKeys = EmptyArray.INT;
mValues = EmptyArray.OBJECT;
} else {
mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity);
mKeys = new int[mValues.length];
}
mSize = 0;
}
1: SparseArray存在两个构造方法, 带参数的构造方法可以指定SparseArray的初始容量,如果使用默认的构造函数,那么初始容量为10。
2:当初始容量为0的时候,mkey和mValue都被初始化了EmptyArray常量,避免重复创建
mKeys = EmptyArray.INT;
mValues = EmptyArray.OBJECT;
3: 当初始容量不为0的时候,初始化 mValues 数组时使用了 ArrayUtils 的 newUnpaddedObjectArray 方法, 这是个native方法,此方法返回的就是一个比约定的容量大小大一些的容量。即: 实际返回的数组大小有可能比 minLength大,也就是说,如果 minLength 是15,那么实际返回的数组大小可能是 16。
3:SparseArray的增:
put 方法用于放入一个整数到对象的映射:
1: binarySearch 二分查找来确定key的位置, 有效长度为mSize
2: 通过二分查找在 mKeys 数组的 mSize 长度中, 查看 key 是否已经存在,如果存在,则直接更新 mValues 数组中对应位置的值
3:key 不存在,那么将二分查找的返回值取反,得到 key 要插入的位置,检查该位置的 value 是否标记为已删除,如果是,直接在该位置更新 key 和 value。
4:如果有必要,进行一次「垃圾回收」,并更新即将插入的位置(垃圾回收过程数组元素发生了移动,所以插入位置可能会变动)
5:在 mKeys 和 mValues 数组中的对应位置分别插入 key 和 value,size 加 1。
为什么将二分查找的返回值按位取反就能得到新元素的位置呢?
如果数组元素为 [-1,0,1,3,6], 如果数组元素为 [-1,0,1,3,6],在使用二分查找法查找元素 5 时,lo 最后的值是4,也就是元素 5 应该在数组中下标为 4 的位置。在经过按位取反后,binarySearch 的返回值就是个负数。而在 put 方法中,通过 i = ~i 可以得到这个位置下标,然后执行更新或插入即可。
备注: 因为Key[] 和 value[] 初始值为null,所以在进行put()操作的时候, 会优先进行一次二分查找进行一下定位,那么key[]在进行若干次put操作后就是一个有序的数组。
4: SparseArray的删:remove and delete(都是public的类型)
delete方法首先使用二分查找对应的key,取得到了对应的位置,并未立即搬除数据,而是把mValues对应位置的元素指向delete, 并将mGarbage设置为true, 意味着有必要进行垃圾回收了,当下次垃圾回收的时候 再统一进行元素的搬移操作。
5:SparseArray的查:
直接通过二分查找去查找key是否在mkey数组中, 如果有则返回mValues数组中的相同位置的值, 如果没有或者mValues中对应位置的元素被标记为已删除,就返回默认值(没有默认值就返回为null)
SparseArray/ArrayMap 代替HashMap的优点
1:SparseArray 相比 HashMap
一是避免了自动装箱过程,
二是去掉了额外的 Entry 包装对象
2:SpareArray 内部采用了两个数组来分别存储 key 和 value,寻找 key 时使用了二分查找。不过,也是由于这个原因,查找效率无法和 HashMap 相比,适用于少量数据的情况。
3:ArrayMap系列跟HashMap的映射
SparseArray => HashMap
LongSparseArray => HashMap
SparseBooleanArray => HashMap
SparseIntArray => HashMap
SparseLongArray => HashMap
ArrayMap => HashMap
ArraySet => HashSet