HashMap 原理:
HashMap 是基于哈希表的 Map 接口实现的,内部存储的结构是使用哈希表的拉链结构(数组+链表)的方式,如下图所示
HashMap中默认的存储大小就是一个容量为16的数组,所以当我们创建出一个HashMap对象时,即使里面没有任何元素,也要分别一块内存空间给它,而且,
我们再不断的向HashMap里put数据时,当达到一定的容量限制时(这个容量满足这样的一个关系时候将会扩容:HashMap中的数据量>容量*加载因子,而
HashMap中默认的加载因子是0.75),HashMap的空间将会扩大,而且扩大后新的空间大约是原来的2倍,只要一满足扩容条件,HashMap的空间将会以2倍
的规律进行增大。假如我们有几十万、几百万条数据,那么HashMap要存储完这些数据将要不断的扩容,在此过程中也需要不断的做hash运算,这将对我们的
内存空间造成很大消耗和浪费。
用pixel手机做测试,存储10万条数据方法如下:
for (int i = 0; i <100000; i++) {
hashMap.put(i,"sunchao = "+i);
}
使用for循环,存储10万条数据,我们可以查看,当前测试APP的内存动态图如下:
测试HashMap存储10万条数据得出的内存动态图:
从抓取的APP的动态的图,我们可以发现,HashMap的消耗的最大内存达到了103.5MB。
ArrayMap的工作原理:
为了解决HashMap占内存的弊端,Android提供了内存效率更高的ArrayMap。它内部使用两个数组进行工作,其中一个数组记录
key换成hash值过后的顺序列表,另外一个数组按key的顺序记录Key-Value值,如下图所示
可以看出ArrayMap采用的是Key-Values映射数据结构。
ArrayMap中主要存储的数据的是两个数据
mHashs中存储出的是每个key的hash值,并且在这些key的hash值在数组当中是从小到大排序的。mArray的数组长度是mHashs的两倍,每两个元素分别
是key和value,这两元素对应mHashs中的hash值。在我们使用put方法进行存储数据的过程中,空间不够时,会发生如下扩容
BASE_SIZE = 4,先判断oSize值是否大于等于8,如果是则n=oSize*1.5,否则就判断是否大于等于4,是则n=8个,否则n=4个。
然后把老的数组中的数据复制到了新的数组当中如下
allocArrays和freeArrays方法中,。这两个方法的作用基本上就是当长度不够用,我们需要废弃掉老的数组,使用新的数组的时候,
把老的数组(包含mHashes和mArray)的数据添加到oArray当中,然后把oldArray赋值给mBaseCache(4个长度),如果再有
新的ArrayMap创建数组空间的时候,如果还是申请4个的空间,那么优先使用缓存下来的这个。
用pixel手机做测试,同样存储10万条数据方法如下:
for (int i = 0; i <100000; i++) {
arrayMap.put(i,"sunchao = "+i);
}
使用for循环,存储10万条数据,我们可以查看,当前测试APP的内存动态图如下:
测试ArrayMap存储10万条数据得出的内存动态图:
从抓取的APP的动态的图,我们可以发现,ArrayMap的消耗的最大内存达到了91.4MB。
从中可以得出结论: 在存储数据方面ArrayMap确实要比HashMap消耗的内存小。HashMap初始值16个长度,每次扩容的时候,
直接申请双倍的数组空间。ArrayMap每次扩容的时候,如果size长度大于8时申请size*1.5个长度,大于4小于8时申请8个,小于
4时申请4个。ArrayMap其实是申请了更少的内存空间,但是扩容的频率会更高,并且ArrayMap采用了一种独特的方式,能够重复
的利用因为数据扩容而遗留下来的数组空间,而HashMap没有这种设计。