简介

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) {//确定新的容量大小nfinal 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的hashCodeif (index >= 0) {return removeAt(index);}return null;
}public V removeAt(int index) {//根绝index找到mArray中对应的值valuefinal 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的主要内容;如有问题,请多指教,谢谢!

ArrayMap的源码分析相关推荐

  1. 【转】HashMap,ArrayMap,SparseArray源码分析及性能对比

    HashMap,ArrayMap,SparseArray源码分析及性能对比 jjlanbupt 关注 2016.06.03 20:19* 字数 2165 阅读 7967评论 13喜欢 43 Array ...

  2. Framework 源码解析知识梳理(5) startService 源码分析

    一.前言 最近在看关于插件化的知识,遇到了如何实现Service插件化的问题,因此,先学习一下Service内部的实现原理,这里面会涉及到应用进程和ActivityManagerService的通信, ...

  3. Android源码分析-全面理解Context

    前言 Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像 ...

  4. Framework学习之路(一)—— UI绘制深入源码分析

    Framework学习之路(一)-- UI绘制深入源码分析 本篇为笔者对Android SDK 33版本的UI绘制入口进行追踪的过程,主要作笔记作用.由于笔者经验尚浅,水平也有限,所以会存在很多不足的 ...

  5. 【安卓 R 源码】 bindService 源码分析

    使用bindService主要分两种情形: 1. Service的调用者client与Service在同一个App中: 2. Service的调用者client是App1中的一个Activity,而S ...

  6. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  7. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  8. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  9. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

最新文章

  1. springboot 做表白墙_学校表白墙有多羞耻??!辣眼分析数千条表白内容,原来脱单秘密在这里.......
  2. GE刚刚换帅,就向艾默生出售智能平台业务?刚刚换帅,GE又向艾默生出售智能平台业务,未来究竟何去何从?...
  3. DeepLearning based on PaddlePaddle系列一
  4. 【转帖】windows命令行中java和javac、javap使用详解(java编译命令)
  5. 利用dft的定义计算dft的matlab程序_CP2K教程系列之静态计算(Pymatflow篇)
  6. 人工智能 - paddlepaddle飞桨 - 深度学习基础教程 - 图像分类
  7. trycatch处理流IO流中的异常
  8. 倪光南:微软故意放任 Windows 盗版;淘宝为性别歧视道歉;乐视亏损 139 亿 | CSDN极客头条...
  9. Dynamips使用说明
  10. 基于51单片机和 ADC0808 ADC0809的自动数字电压表proteus仿真程序设计
  11. 用c语言实现窗口化,c怎么窗口化只能开挂吗
  12. alibaba人一起写过的技术丛书
  13. ajax传递数组,后台接收
  14. N 上CR SPCSS00383586
  15. 22个关于ES6的最新面试题汇总整理
  16. kali破解pdf密码
  17. 求求你别再做低级的码农了!计算机专业的必看,这样做才能有出路!
  18. 浅谈工资合理避税,五大妙招轻松搞定!
  19. html-5表白神器源码,C# 表白神器源码(winform)
  20. 瑞隆源RUILON静电esd二极管

热门文章

  1. STM32CubeMX Crystal/Ceramic Resontor
  2. Bq769XX IIC 通讯 ALERT引脚
  3. 牛客多校第八场 Enigmatic Partition(DP)
  4. 轻量化固态激光雷达的三维定位与建图
  5. 量子信息提供后摩尔时代技术,超导量子电路中量子生成对抗学习
  6. deepin、UOS应用软件标题栏大额头问题解决
  7. mysql 双1设置_mysql 双1设置
  8. R语言VaR市场风险计算方法与回测、用LOGIT逻辑回归、PROBIT模型信用风险与分类模型...
  9. 通过FinalShell连接AWS的EC2服务器
  10. Javascript(jQuery)中绑定页面上所有按钮点击事件的几种方式