Java开发过程中,一般免不了使用HashMap来存储键值对以方便各种需求的实现,但是对于Android这种对内存非常敏感的移动平台,很多时候使用一些Java的API并不能达到更好的性能,相反反而更消耗内存,所以针对Android这种移动平台,也推出了更符合自己的API,比如SparseArray、ArrayMap用来代替HashMap在有些情况下能带来更好的性能提升。

对于SparseArray,比HashMap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为Integer类型等),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,为了优化性能,它内部对数据还采取了压缩的方式来表示稀疏数组的数据,同时,还实现了SparseIntArray、SparseLongArray、SparseBooleanArray。使用上,几乎和HashMap的API无异,这里仅以SparseArray为例:

常用API

添加数据

public void put(int key, E value)

删除数据

public void remove(int key)

或者

public void delete(int key)

这里remove只是delete的一个alias,没任何差异。注意:仅SparseArray实现了remove,其余几个实现则只有delete,可能SparseArray只是为了保持与Map的API相似吧。

获取数据

public E get(int key)

或者

public E get(int key, E valueIfKeyNotFound)

顾名思义,后者是查询不到时的返回值。注意:这里面SparseArray使用get的时候查找不到时返回为null,而SparseIntArray、SparseLongArray则默认返回0,SparseBooleanArray默认返回false。

特有方法

获取对应的key:

public int keyAt(int index)

获取对应的value:

public E valueAt(int index)

获取对应的key所在位置索引,由于采用二分法查找键的位置,所以没有的话返回小于0的数值,而不是返回-1。注意:返回的负数其实是表示它在哪个位置就找不到了,如果你存了5个,查找的键大于5个值的话,返回就是-6

public int indexOfKey(int key)

获取对应的value所在位置索引,不存在则返回-1

public int indexOfValue(E value)

应用场景

虽说SparseArray性能比较好,但是由于其添加、查找、删除数据都需要先进行一次二分查找,所以在数据量大的情况下性能并不明显,将降低至少50%。

满足下面两个条件我们可以使用SparseArray代替HashMap:

1、数据量不大,最好在千级以内

2、key必须为Integer类型

如果key为非Integer且数据量在千级以下,可以选择ArrayMap替代HashMap,这里不再,内部实现和SparseArray类似并存储了Hash值,这里不再介绍。

回顾总结

博主最近就是被SparseIntArray坑了一把,女票公司最近给她接一个app的维护,貌似是前人听了android studio的建议,把HashMap换成了SparseIntArray,存储的是整型的键和值,因为存储的是ListView的索引,值为ListView的状态,展开或折叠,展开存1折叠存0,而这货又不看代码,判断SparseIntArray是否有特定key的时候,使用了keyAt来判断,事实上这个非常容易引起数组越界,而SparseIntArray又默认初始化10个长度,再加上早期app数据不超过10个,后面数据超过10之后,就爆出问题了。这个问题给我看,我第一想法是,既然越界,就判断呗,然后就判断了传入key是否超过数组长度,看似没问题,一天后又测出,ListView状态不能切换,这才仔细阅读了一下源码,发现这里实际上该使用indexOfKey,判断小于0的则为不存在,大于0则为存在,又或者,这里面实际根本不用判断,因为SparseIntArray找不到对应key的情况返回的就是我们需要返回的0,所以,我们每次indexOfKey(内部和get一样,都会先查询)去查找一次,反而是浪费了时间。这里不得不吐槽下这个代码的作者,如果对于不熟悉的API没有透析,还是不要乱用得好,免得误导后面接手的哈。

补充一点,如果是使用SparseArray替代HashMap,其实是可以完全按照HashMap的使用方式来使用的,因为SparseArray的get方法找不到的时候默认返回null,这个不像SparseIntArray等实现,返回值一般和我们正常需求值没有太大异议,我们完全可以通过判断get得到的值是否为null来判断key是否存在,换句话说,我们简单替换HashMap为SparseArray时,只需替换声明即可,后面API可以完全覆盖。

简单总结一下,方便新人。