安卓性能优化—使用ArrayMap与SparseArray

性能优化是我们做开发的必须要熟练掌握的技能,所以我打算写一个性能优化专题,把平时用到的一些优化方法记录下来,以便忘记的时候可以快速查找,同时也给给其他开发者提供微薄之力吧:这篇文章讲述的是在一些**特定的场景**使用使用ArrayMap与SparseArray代替HashMap,提高对数据的操作;先看看官方文档的描述:

ArrayMap is a generic key->value mapping data structure that is designed to be more memory efficient than a traditional HashMap, this implementation is a version of the platform's ArrayMap that can be used on older versions of the platform.SparseArrays map integers to Objects. Unlike a normal array of Objects, there can be gaps in the indices. It is intended to be more memory efficient than using a HashMap to map Integers to Objects, both because it avoids auto-boxing keys and its data structure doesn't rely on an extra entry object for each mapping.Note that for containers holding up to hundreds of items, the performance difference is not significant, less than 50%.

从官方文档可以看出使用ArrayMap与SparseArray都要比传统的HashMap 更有效率;但是最后有一个note,也就是当数据量达到千级以上的时候,ArrayMap与SparseArray都要比传统的HashMap 效率更低50%;

HashMap

HashMap允许使用 null 值和 null 键,是基于hashing原理,我们通过put()和get()方法储存和获取对象。HashMap的结构:

安卓性能优化—使用ArrayMap与SparseArray_第1张图片

HashMap结构

HashMap 有两个参数影响其性能:初始容量 和加载因子。容量是哈希表中桶的数量(默认16组),初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。加载因子默认值为0.75 。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用LinkedList来解决碰撞问题,当发生碰撞了,对象将会储存在LinkedList的下一个节点中。 HashMap在每个LinkedList节点中储存键值对对象。

put()方法

安卓性能优化—使用ArrayMap与SparseArray_第2张图片

当两个Key同时hash到一个值时,就会出现这样的冲突。这个冲突主要有2种解决方法。

1、开放地址,亦即如果hash冲突,则在空闲的位置进行插入

2、hash复用,同一个hash值,链式地加入多个value

get()方法


安卓性能优化—使用ArrayMap与SparseArray_第3张图片

HashMap还有很多方法,这里就不一一例举了;


安卓性能优化—使用ArrayMap与SparseArray_第4张图片

通过get与put的源码,可以看出HashMap获取数据是通过遍历Entry[]数组来得到对应的元素,在数据量很大时候会比较慢,所以在Android中,HashMap是比较费内存的,我们在一些情况下可以使用SparseArray和ArrayMap来代替HashMap。

SparseArray


安卓性能优化—使用ArrayMap与SparseArray_第5张图片

可以看出SparseArray由两个数组mKeys和mValues存放;其中key的类型为int型,这就显得SparseArray比HashMap更省内存一些,SparseArray在存储和读取数据时候,使用的是二分查找法,那何为二分法呢?

先看一下put()方法:


安卓性能优化—使用ArrayMap与SparseArray_第6张图片

在上面代码中有一个ContainerHelpers.binarySearch(mKeys, mSize, key)方法,这是Arrays提供了一个方便查询的方法,也就是我们所说的“二分法”;


安卓性能优化—使用ArrayMap与SparseArray_第7张图片

get()方法:


安卓性能优化—使用ArrayMap与SparseArray_第8张图片

使用二分查找法和之前的key比较当前我们添加的元素的key的大小,然后按照从小到大的顺序排列好,所以,SparseArray存储的元素都是按元素的key值从小到大排列好的。而在获取数据的时候,也是使用二分查找法判断元素的位置,所以,在获取数据的时候非常快,比HashMap快的多,因为HashMap获取数据是通过遍历Entry[]数组来得到对应的元素。

其他的一些方法:


安卓性能优化—使用ArrayMap与SparseArray_第9张图片

ArrayMap

ArrayMap是一个键值对映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值,它和SparseArray一样,也会对key使用二分法进行从小到大排序,区别是ArrayMap的key是hash值,


安卓性能优化—使用ArrayMap与SparseArray_第10张图片

可以看出ArrayMap的构造方法直接调用的父类的构造方法:


安卓性能优化—使用ArrayMap与SparseArray_第11张图片

put()方法:


安卓性能优化—使用ArrayMap与SparseArray_第12张图片

get()方法与SparseArray的一样,就不哆嗦了:

总结

因为ArrayMap与SparseArray内部都使用了二分法进行从小到大的排序,所以当数据量很大的时候,效率至少降低一半,所以谷歌推荐数据量在千级以内时使用ArrayMap与SparseArray,数据量非常大时使用HashMap;

你可能感兴趣的:(安卓性能优化—使用ArrayMap与SparseArray)