SparseArray是 Android框架独有的类,在标准的JDK中不存在这个类。它要比 HashMap 节省内存,某些情况下比HashMap性能更好,按照官方问答的解释,主要是因为SparseArray不需要对key和value进行auto- boxing(将原始类型封装为对象类型,比如把int类型封装成Integer类型),结构比HashMap简单(SparseArray内部主要使用 两个一维数组来保存数据,一个用来存key,一个用来存value)不需要额外的额外的数据结构(主要是针对HashMap中的HashMapEntry 而言的)。是骡子是马得拉出来遛遛,下面我们就通过几段程序来证明SparseArray在各方面表现如何,下面的试验结果时在我的Hike X1(Android 4.2.2)手机上运行得出的。

代码1:

int MAX = 100000;
long start = System.currentTimeMillis();
HashMap<Integer, String> hash = new HashMap<Integer, String>();
for (int i = 0; i < MAX; i++) {
hash.put(i, String.valueOf(i));
}
long ts = System.currentTimeMillis() - start;

代码2:

int MAX = 100000;
long start = System.currentTimeMillis();
SparseArray<String> sparse = new SparseArray<String>();
for (int i = 0; i < MAX; i++) {
sparse.put(i, String.valueOf(i));
}
long ts = System.currentTimeMillis() - start;

我们分别在long start处和long ts处设置断点,然后通过DDMS工具查看内存使用情况。

代码1中,我们使用HashMap来创建100000条数据,开始创建前的系统内存情况为:

创建HashMap之后,应用内存情况为:

可见创建HashMap用去约 13.2M内存

再看 代码2,同样是创建100000条数据,我们用SparseArray来试试,开始创建前的内存使用情况为:

创建SparseArray之后的情况

可见使用 SparseArray 的确比 HashMap 节省内存,大概节省 35%左右的内存。

我们再比较一下插入数据的效率如何,我们在加两段代码(主要就是把插入顺序变换一下,从大到小插入):

代码3:

int MAX = 100000;
long start = System.currentTimeMillis();
HashMap<Integer, String> hash = new HashMap<Integer, String>();
for (int i = 0; i < MAX; i++) {
hash.put(MAX - i -1, String.valueOf(i));
}
long ts = System.currentTimeMillis() - start;

代码4:

int MAX = 100000;
long start = System.currentTimeMillis();
SparseArray<String> sparse = new SparseArray<String>();
for (int i = 0; i < MAX; i++) {
sparse.put(MAX - i -1, String.valueOf(i));
}
long ts = System.currentTimeMillis() - start;

我们分别把这4代码分别运行5次,对比一下ts的时间(单位毫秒):

# 代码1 代码2 代码3 代码4
1 10750ms 7429ms 10862ms 90527ms
2 10718ms 7386ms 10711ms 87990ms
3 10816ms 7462ms 11033ms 88259ms
4 10943ms 7386ms 10854ms 88474ms
5 10671ms 7317ms 10786ms 90630ms

通过结果我们看出,在正序插入数据时候,SparseArray比HashMap要快一些;HashMap不管是倒序还是正序开销几乎是一样的;但是SparseArray的倒序插入要比正序插入要慢10倍以上,这时为什么呢?我们再看下面一段代码:

SparseArray<String> sparse = new SparseArray<String>(3); 
sparse.put(1, "s1");
sparse.put(3, "s3");
sparse.put(2, "s2");

我们在Eclipse的debug模式中,看Variables窗口,如图:

及时我们是按照1,3,2的顺序排列的,但是在SparseArray内部还是按照正序排列的,这时因为SparseArray在检索数据的时候使用的是二分查找,所以每次插入新数据的时候SparseArray都需要重新排序,所以代码4中,逆序是最差情况。

下面我们在简单看下检索情况:

代码5:

long start4search = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
hash.get(33333); //针对固定值检索
}
long end4search = System.currentTimeMillis() - start4search;

代码6:

long start4search = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
hash.get(i); //顺序检索
}
long end4search = System.currentTimeMillis() - start4search;

代码7:

long start4search = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
sparse.get(33333); //针对固定值检索
}
long end4search = System.currentTimeMillis() - start4search;

代码8:

long start4search = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
sparse.get(i); //顺序检索
}
long end4search = System.currentTimeMillis() - start4search;
# 代码5 代码6 代码7 代码8
1 4072ms 4318ms 3442ms 3390ms
2 4349ms 4536ms 3402ms 3420ms
3 4599ms 4203ms 3472ms 3376ms
4 4149ms 4086ms 3429ms 3786ms
5 4207ms 4219ms 3439ms 3376ms

代码9,我们试一些离散的数据。

//使用Foo为了避免由原始类型被自动封装(auto-boxing,比如把int类型自动转存Integer对象类型)造成的干扰。
class FOO{
Integer objKey;
int intKey;
}
...
int MAX = 100000; HashMap<Integer, String> hash = new HashMap<Integer, String>();
SparseArray<String> sparse = new SparseArray<String>(); for (int i = 0; i < MAX; i++) {
hash.put(i, String.valueOf(i));
sparse.put(i, String.valueOf(i));
} List<FOO> keylist4search = new ArrayList<FOO>();
for (int i = 0; i < MAX; i++) {
FOO f = new FOO();
f.intKey = i;
f.objKey = Integer.valueOf(i);
keylist4search.add(f);
} long start4search = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
hash.get(keylist4search.get(i).objKey);
}
long end4searchHash = System.currentTimeMillis() - start4search; long start4search2 = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
sparse.get(keylist4search.get(i).intKey);
}
long end4searchSparse = System.currentTimeMillis() - start4search2;

代码9,运行5次之后的结果如下:

表2:

# end4searchHash end4searchSparse
1 2402ms 4577ms
2 2249ms 4188ms
3 2649ms 4821ms
4 2404ms 4598ms
5 2413ms 4547ms

从上面两个表中我们可以看出,当SparseArray中存在需要检索的下标时,SparseArray的性能略胜一筹(表1)。但是当检索的下标 比较离散时,SparseArray需要使用多次二分检索,性能显然比hash检索方式要慢一些了(表2),但是按照官方文档的说法性能差异不是很大,不 超过50%( For containers holding up to hundreds of items, the performance difference is not significant, less than 50%.)

总体而言,在Android这种内存比CPU更金贵的系统中,能经济地使用内存还是上策,何况SparseArray在其他方面的表现也不算差(另外,SparseArray删除数据的时候也做了优化——使用了延迟整理数组的方法,可参考官方文档SparseArray,读者可以自行把代码9中的hash.get和sparse.get改成hash.remove和sparse.delete试试,你会发现二者的性能相差无几)。而且,使用SparseArray代替HashMap也是官方推荐的做法,在Eclipse中也会提示你优先使用SparseArray,

另外,我们还可以用 LongSparseArray来替代HashMap。SparseBooleanArray来替代HashMap。

http://android-performance.com/android/2014/02/10/android-sparsearray-vs-hashmap.html

SparseArray替代HashMap来提高性能的更多相关文章

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

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

  2. 73.Android之SparseArray替代HashMap

    转载:https://liuzhichao.com/p/832.html HashMap是java里比较常用的一个集合类,我比较习惯用来缓存一些处理后的结果.最近在做一个Android项目,在代码中定 ...

  3. 【移动开发】SparseArray替代HashMap

    SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的class,目的是提高效率,其核心是折半查找函数(binarySearch). p ...

  4. 关于Android中ArrayMap/SparseArray比HashMap性能好的深入研究

    由于网上有朋友对于这个问题已经有了很详细的研究,所以我就不班门弄斧了: 转载于:http://android-performance.com/android/2014/02/10/android-sp ...

  5. 【转】HashMap,ArrayMap,SparseArray源码分析及性能对比

    HashMap,ArrayMap,SparseArray源码分析及性能对比 jjlanbupt 关注 2016.06.03 20:19* 字数 2165 阅读 7967评论 13喜欢 43 Array ...

  6. Java编程提高性能时需注意的地方

    1.尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面 第一,控制资源的使用,通过线程同步来控制资 ...

  7. Java编程 “提高性能” 应尽力做到

    除了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于随意化,这些不好的习惯或对程序语言的不了解是应该好好打压打压了. 下面是参考网络资源总结的一些在Java编程中尽可能要做到的 ...

  8. 用SparseArray代替HashMap

    SparseArray是android提供的一个工具类,它可以用来替代hashmap进行对象的存储,其内部实现了一个矩阵压缩算法,很适合存储稀疏矩阵的. PS:support包中还提供了兼容的类Spa ...

  9. SparseArray代替HashMap

    相信大家都明白,手机软件的开发不同于PC软件的开发,因为手机性能相对有限,内存也有限,所谓“寸土寸金”,可能稍有不慎,就会导致性能的明显降低.Android为了方便开发者,特意在android.uti ...

随机推荐

  1. cakephp目录结构

  2. XML DTD语法详解

    XML DTD详解   XML DTD详解 前情提要与本文内容介绍 前面的两篇XML相关博文: 第一篇是介绍格式正规的XML: 格式正规的XML:语法 属性 实体 处理指令 样式单 CDATA节 第二 ...

  3. 关于用Date类计算活了多少天和用Calendar类计算闰年的demo

    在javaSE阶段,Date类和Calendar类以后会经常用到 这两个类当中的一些常用方法 通过两个demo 进行学习和练习 第一个要求如下:让用户自己输入yyyy-MM-dd 格式的年月日 然后得 ...

  4. 防恶意解析,禁止用IP访问网站的Apache设置 修改 httpd.conf 实现

    一般来说,网站可以用域名和IP来访问.你的网站可以通过IP直接访问,本来这没什么问题,但是会有些隐患: 由于搜索引擎也会收录你的IP地址的页面,所以同一个页面搜索引擎会重复收录,造成页面的权重不如单个 ...

  5. java Linkedhashmap源码分析

    LinkedHashMap类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是插入次序,或者是最近最少使用(LRU)的次序.只比HashMap慢一点:而在迭代访问时反而更快,因为它使用链表维 ...

  6. C# SendMessage用法一二

    函数功能:该函数将指定的消息发送到一个或多个窗口.此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回.而函数PostMessage不同,将一个消息寄送到一个线程的消息队列后立即返回.  函数 ...

  7. Vistual Studio的导出模板功能

    应用场景,每个项目有自己固定的目录结构和引用文件, 无需每次创建一个项目,就手工一一将那些目录再new一遍.如图所示 菜单  文件=>导出模板 之后的操作基本上一路"下一步" ...

  8. 免费证书申请——Let's Encrypt的申请与应用(IIS,Tomcat)

    环境 Windows Server 2008 R2 Tomcat 8.5.31 JDK8 利用IIS+letsencrypt-win-simple.V1.9.1申请免费SSL证书 新建一个IIS空网站 ...

  9. ajaxs

    AJAX 是一种独立于 Web 服务器软件的浏览器技术.AJAX 基于下列 Web 标准:JavaScript XML HTML CSS 在 AJAX 中使用的 Web 标准已被良好定义,并被所有的主 ...

  10. 「BZOJ1010」[HNOI2008] 玩具装箱toy(斜率优化)

    P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为 1⋯N1\cdots N1⋯N ...