一个数组存的是key的hash值,一个存在的key value。结构以下。java
嗯,如标题所言是非线程安全的,多个线程共享ArrayMap的时候就有数据不一致的问题。算法
提及来很简单,就是往ArrayMap中存键值对,若是要存的这个key在HashMap中已经存在了的就用新值替换掉,若是不存在key就连值带键都存在里面。 大致的过程是这样的。设计模式
当ArrayMap的容量已满的时候就要使用扩容机制了,具体怎么扩呢源码中找答案。数组
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);
...
复制代码
一波三目运算道出真理:缓存
没什么好说的 什么底层源码 什么设计模式 一梭子代码安全
@Override
public V get(Object key) {
final int index = indexOfKey(key);
return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}
复制代码
删除操做,本质就是对数组进行复制操做,正常状况下,bash
当ArrayMap中的元素数小于容量的1/3的时候启动缩容模式,缩容到当期数量(不是容量)的1.5倍,若是数量小于8直接扩容到8。ide
if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
final int n = osize > (BASE_SIZE*2) ? (osize + (osize>>1)) : (BASE_SIZE*2);
....
}
复制代码
static Object[] mBaseCache;//容量为4,ArrayMap缓存链表
static int mBaseCacheSize;//容量为4的缓存数量
static Object[] mTwiceBaseCache;//容量为8的Array缓存链表
static int mTwiceBaseCacheSize;//容量为8的缓存数量
复制代码
private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
//当前hashs数据的长度为8
if (hashes.length == (BASE_SIZE*2)) {
...
} else if (hashes.length == BASE_SIZE) {
synchronized (ArrayMap.class) {
if (mBaseCacheSize < CACHE_SIZE) {
array[0] = mBaseCache;//把array的第一个元素指向当前的mBaseCache
array[1] = hashes;//把array的第二个元素指向hash数组
for (int i=(size<<1)-1; i>=2; i--) {//把第二个之后的元素都置空
array[i] = null;
}
mBaseCache = array;//mBaseCache执行array
mBaseCacheSize++;//数字加1
}
}
}
}
复制代码
看注释,应该就明白了,若是还不明白看看下面的这张图。ui
关注下,freeArrays()方法中的synchronized关键字,不是说好的线程不安全吗,怎么还有出现synchronized呢google
线程安全问题的本质是,可变数据在多条线程中共享。 mBaseCache和mTwiceBaseCache都是静态的,也就是说会存在他们多个ArrayMap对象访问他们的状况。反过来思考,一个ArrayMap对象,在单一线程中建立使用,扩容缩容的时候若是没有加同步块会出现线程安全问题。
就缓存两种容量的map,容量为4和容量为8的
private void allocArrays(final int size) {
if (size == (BASE_SIZE*2)) {
//当要分配数组的容量为8时
...
} else if (size == BASE_SIZE) {
//当分配数组的容量为4时
synchronized (ArrayMap.class) {
if (mBaseCache != null) {
final Object[] array = mBaseCache;
mArray = array;//mArray指向缓存链表中第一个数组
mBaseCache = (Object[])array[0];//mBaseCache指向下一个数组
mHashes = (int[])array[1];//mHashes数组指向arry的第一个元素
array[0] = array[1] = null;//置空
mBaseCacheSize--;
return;
}
}
}
mHashes = new int[size];
mArray = new Object[size<<1];
}
复制代码
google官方建议当数据量小于1000的时候,推荐使用ArrayMap,但于1000使用HashMap。ArrayMap查询效率为O(logN),删除和添加元素的时候都要移动数组效率较低 。HashMap的查询更改速度为O(1)。可是ArrayMap优点就是省内存!
除了ArrayMap以外还有SpareArray,原理和ArrayMap差很少,但他的key为int类型,避免了基本数据类型的拆箱装箱带来的效率问题。当肯定value的值为int/long/boolean的时候就可使用SparseIntArray/SparseLongArray/SpareseBoolean。
补充一个二分查找算法
public static int binarySearch(int[] array,int value){
if (array == null){
throw new IllegalArgumetsException("不能为空");
}
int lo = 0;
int li = array.length - 1;
while(lo<=li){
int index = (lo+li)/2;
int midValue = array[index];
if(value>midValue){
li= index+1;
}else if(value<midValue){
lo = index-1;
}else{
return index;
}
}
return ~lo;
}
复制代码