文档章节

Android应用性能优化之使用SparseArray替代HashMap

丁佳辉
 丁佳辉
发布于 2017/02/07 10:31
字数 1181
阅读 7
收藏 0

HashMap是java里比较常用的一个集合类,我比较习惯用来缓存一些处理后的结果。最近在做一个Android项目,在代码中定义这样一个变量,实例化时,Eclipse却给出了一个 performance 警告。

sparsearray

 

意思就是说用SparseArray <E> 来替代,以获取更好性能。老实说,对SparseArray并不熟悉,第一感觉应该是Android提供的一个类。按住Ctrl点击进入SparseArray的源码,果不其然,确定是Android提供的一个工具类。

android_util_sparsearray

 

单纯从字面上来理解,SparseArray指的是稀疏数组(Sparse array) ,所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容。

假设有一个9*7的数组,其内容如下:

sparsearray_pic1

 

在此数组中,共有63个空间,但却只使用了5个元素,造成58个元素空间的浪费。以下我们就使用稀疏数组重新来定义这个数组:

sparsearray_pic2.jpg

 

其中在稀疏数组中第一部分所记录的是原数组的列数和行数以及元素使用的个数、第二部分所记录的是原数组中元素的位置和内容。经过压缩之后,原来需要声明大小为63的数组,而使用压缩后,只需要声明大小为6*3的数组,仅需18个存储空间。

继续阅读SparseArray的源码,从构造方法我们可以看出,它和一般的List一样,可以预先设置容器大小,默认的大小是10:

 
  1. public SparseArray() {
  2. this(10);
  3. }
  4.  
  5. public SparseArray(int initialCapacity) {
  6. initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
  7.  
  8. mKeys = new int[initialCapacity];
  9. mValues = new Object[initialCapacity];
  10. mSize = 0;
  11. }

再来看看它对数据的”增删改查”。

它有两个方法可以添加键值对:

 
  1. public void put(int key, E value) {}
  2. public void append(int key, E value){}

有四个方法可以执行删除操作:

 
  1. public void delete(int key) {}
  2. public void remove(int key) {} //直接调用的delete(int key)
  3. public void removeAt(int index){}
  4. public void clear(){}

修改数据起初以为只有setValueAt(int index, E value)可以修改数据,但后来发现put(int key, E value)也可以修改数据,我们查看put(int key, E value)的源码可知,在put数据之前,会先查找要put的数据是否已经存在,如果存在就是修改,不存在就添加。

 
  1. public void put(int key, E value) {
  2. int i = binarySearch(mKeys, 0, mSize, key);
  3.  
  4. if (i > = 0) {
  5. mValues[i] = value;
  6. } else {
  7. i = ~i;
  8.  
  9. if (i < mSize &amp;&amp; mValues[i] == DELETED) {
  10. mKeys[i] = key;
  11. mValues[i] = value;
  12. return;
  13. }
  14.  
  15. if (mGarbage &amp;&amp; mSize > = mKeys.length) {
  16. gc();
  17.  
  18. // Search again because indices may have changed.
  19. i = ~binarySearch(mKeys, 0, mSize, key);
  20. }
  21. …………

所以,修改数据实际也有两种方法:

 
  1. public void put(int key, E value)
  2. public void setValueAt(int index, E value)

最后再来看看如何查找数据。有两个方法可以查询取值:

 
  1. public E get(int key)
  2. public E get(int key, E valueIfKeyNotFound)

其中get(int key)也只是调用了 get(int key,E valueIfKeyNotFound),最后一个从传参的变量名就能看出,传入的是找不到的时候返回的值.get(int key)当找不到的时候,默认返回null。

查看第几个位置的键:public int keyAt(int index)
有一点需要注意的是,查看键所在位置,由于是采用二分法查找键的位置,所以找不到时返回小于0的数值,而不是返回-1。返回的负值是表示它在找不到时所在的位置。

查看第几个位置的值:
public E valueAt(int index)
查看值所在位置,没有的话返回-1:
public int indexOfValue(E value)
最后,发现其核心就是折半查找函数(binarySearch),算法设计的很不错。

 
  1. private static int binarySearch(int[] a, int start, int len, int key) {
  2. int high = start + len, low = start - 1, guess;
  3.  
  4. while (high - low > 1) {
  5. guess = (high + low) / 2;
  6.  
  7. if (a[guess] < key)
  8. low = guess;
  9. else
  10. high = guess;
  11. }
  12.  
  13. if (high == start + len)
  14. return ~(start + len);
  15. else if (a[high] == key)
  16. return high;
  17. else
  18. return ~high;
  19. }

相应的也有SparseBooleanArray,用来取代HashMap <Integer, Boolean> ,SparseIntArray用来取代HashMap <Integer, Integer> ,大家有兴趣的可以研究。

总结:
SparseArray是android里为<Interger,Object> 这样的Hashmap而专门写的类,目的是提高效率,其核心是折半查找函数(binarySearch)。在Android中,当我们需要定义
HashMap <Integer, E> hashMap = new HashMap <Integer, E> ();
时,我们可以使用如下的方式来取得更好的性能.
SparseArray <E> sparseArray = new SparseArray <E> ();

注:

文中关于稀疏数组(Sparse array)的定义说明参照至:
http://hi.baidu.com/piaopiao_0423/item/d8cc2b99729f8380581461d1

本文转载自:https://liuzhichao.com/p/832.html

共有 人打赏支持
丁佳辉
粉丝 19
博文 402
码字总数 196997
作品 0
浦东
程序员

暂无文章

内存模型是怎么解决缓存一致性的?

在再有人问你Java内存模型是什么,就把这篇文章发给他。这篇文章中,我们介绍过关于Java内容模型的来龙去脉。 我们在文章中提到过,由于CPU和主存的处理速度上存在一定差别,为了匹配这种差距...

Java填坑之路
20分钟前
1
0
vue-cli 3.0 初体验

最近复习了下vue,突然发现vue-cli已经更新到3.0版本了,并且变化蛮大,看来要不停的学习,真是一入前端深似海。 安装步骤: 1、全局安装 npm install -g @vue/cli Vue CLI 的包名称由 vue-...

tianyawhl
22分钟前
1
0
Angular进阶之路

【初级】会写页面,能出东西。 给定环境和 rest API,不用第三方库,能在十分钟内完成一个 master/detail 结构的带路由的应用(可以不管美观)。 知识点:Angular CLI、组件、路由、HTTP 服务...

陆小七的主页
24分钟前
0
0
Redis缓存数据库安全加固指导(一)

背景 在众多开源缓存技术中,Redis无疑是目前功能最为强大,应用最多的缓存技术之一,参考2018年国外数据库技术权威网站DB-Engines关于key-value数据库流行度排名,Redis暂列第一位,但是原生...

中间件小哥
24分钟前
0
0
百万级数据mysql分区

1. 什么是表分区? 表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。 2. 表分区与分表的区别 分表...

罗文浩
27分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部