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)手机上运行得出的。 html
代码1: java
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: android
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来试试,开始建立前的内存使用状况为: less
建立SparseArray以后的状况: 建立SparseArray共用去 8.626M内存。 工具
可见使用 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倍以上,这时为何呢?咱们再看下面一段代码:
代码5:
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;
表1:
# | 代码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。