在Android开发时,我们使用的大部分都是Java的api,比如HashMap这个api,使用率非常高,但是对于Android这种对内存非常敏感的移动平台,很多时候使用一些java的api并不能达到更好的性能,相反反而更消耗内存,所以针对Android这种移动平台,也推出了更符合自己的api,比如SparseArray、ArrayMap用来代替HashMap在有些情况下能带来更好的性能提升。
SparseArray是一个适用于Key为Int,且数据量较小情况下(千级以内),为了优化内存占用,从而替代HashMap的工具。
其实是一种以时间换取空间的做法
示例:
HashMap是java里比较常用的一个集合类,我们一般用来缓存一些处理后的结果。但当你做一个Android项目时,在代码中定义这样一个变量,实例化时,AS却给出了一个 performance 警告。
意思是说Map已经不用了,使用SparseArray代替,以获取更好性能。为什么用SparseArray呢,单从字面意思,SparseArray就是稀疏数组。
所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容。
假设有一个9*7的数组,其内容如下:
图 1 二维数组示例
在此数组中,共有63个空间,但却只使用了5个元素,造成58个元素空间的浪费。以下我们就使用稀疏数组重新来定义这个数组:
图 2 使用稀疏数组进行压缩
其中在稀疏数组中第一部分所记录的是原数组的列数和行数以及元素使用的个数、第二部分所记录的是原数组中元素的位置和内容。经过压缩之后,原来需要声明大小为63的数组,而使用压缩后,只需要声明大小为6*3的数组,仅需18个存储空间。
SparseArray相对HashMap节省内存的几点优化:
1、压缩了存储空间
2、避免了自动装箱(auto-boxing)
节省了由于自动装箱而创建的对象内存空间
3、SparseArray不需要开辟内存空间来额外存储外部映射,节省内存
关于存储空间压缩
HashMap内部是使用一个默认容量为16的数组来存储数据的,而数组中每一个元素却又是一个链表的头结点,所以,更准确的来说,HashMap内部存储结构是使用哈希表的拉链结构(数组+链表),所以当我们创建出一个HashMap对象时,即使里面没有任何元素,也要分别一块内存空间给它,而且,我们再不断的向HashMap里put数据时,当达到一定的容量限制时(这个容量满足这样的一个关系时候将会扩容:HashMap中的数据量>容量*加载因子,而HashMap中默认的加载因子是0.75),我们可以看put()方法中有这样的一行代码:
int newCapacity = oldCapacity * 2;
只要一满足扩容条件,HashMap的空间将会以2倍的规律进行增大。假如我们有几十万、几百万条数据,那么HashMap要存储完这些数据将要不断的扩容,而且在此过程中也需要不断的做hash运算,这将对我们的内存空间造成很大消耗和浪费,而且HashMap获取数据是通过遍历Entry[]数组来得到对应的元素,在数据量很大时候会比较慢,所以在Android中,HashMap是比较费内存的,我们在一些情况下可以使用SparseArray和ArrayMap来代替HashMap。
关于外部映射
HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置,存放的都是数组元素的引用,通过每个对象的hash值来映射对象。而SparseArray则是用数组数据结构来保存映射,然后通过折半查找来找到对象。但其实一般来说,SparseArray执行效率比HashMap要慢一点,因为查找需要折半查找,而添加删除则需要在数组中执行,而HashMap都是通过外部映射。但相对来说影响不大,最主要是SparseArray不需要开辟内存空间来额外存储外部映射,从而节省内存。
元素存取
HashMap获取数据是通过遍历Entry[]数组来得到相应的元素,SparseArray通过二分查找来获取对应的元素,一般来说,SparseArray执行效率比HashMap要慢一点,因为查找需要折半查找,而添加删除则需要在数组中执行,而HashMap都是通过外部映射。但相对来说,在千级以内的情况下影响不大。
SparseArray应用场景:
虽说SparseArray性能比较好,可是由于其加入、查找、删除数据都须要先进行一次二分查找。所以在数据量大的情况下性能并不明显,将减少至少50%。
满足下面两个条件我们能够使用SparseArray取代HashMap:
数据量不大,最好在千级以内
key必须为int类型,这中情况下的HashMap能够用SparseArray取代:
HashMap<Integer, Object> map = new HashMap<>();
用SparseArray取代:
SparseArray<Object> array = new SparseArray<>();
SparseArray和ArrayMap都差不多,使用哪个呢?
假设数据量都在千级以内的情况下:
1、如果key的类型已经确定为int类型,那么使用SparseArray,因为它避免了自动装箱的过程,如果key为long类型,它还提供了一个LongSparseArray来确保key为long类型时的使用
2、如果key类型为其它的类型,则使用ArrayMap
参考
https://blog.csdn.net/xiangzhihong8/article/details/51094318 HashMap原理解析
https://blog.csdn.net/qq1263292336/article/details/78871751