广告
返回顶部
首页 > 资讯 > 移动开发 >Android中SparseArray性能优化的使用方法
  • 194
分享到

Android中SparseArray性能优化的使用方法

方法优化Android 2022-06-06 08:06:50 194人浏览 泡泡鱼
摘要

之前一篇文章研究完横向二级菜单,发现其中使用了SparseArray去替换HashMap的使用.于是乎自己查了一些相关资料,自己同时对性能进行了一些测试。首先先说一下Spars

之前一篇文章研究完横向二级菜单,发现其中使用了SparseArray去替换HashMap的使用.于是乎自己查了一些相关资料,自己同时对性能进行了一些测试。首先先说一下SparseArray的原理.

  SparseArray(稀疏数组).他是Android内部特有的api,标准的jdk是没有这个类的.在Android内部用来替代HashMap<Integer,E>这种形式,使用SparseArray更加节省内存空间的使用,SparseArray也是以key和value对数据进行保存的.使用的时候只需要指定value的类型即可.并且key不需要封装成对象类型.

  楼主根据亲测,SparseArray存储数据占用的内存空间确实比HashMap要小一些.一会放出测试的数据在进行分析。我们首先看一下二者的结构特性.

  HashMap是数组和链表的结合体,被称为链表散列.

  SparseArray是单纯数组的结合.被称为稀疏数组,对数据保存的时候,不会有额外的开销.结构如下:

  这就是二者的结构,我们需要看一下二者到底有什么差异...

  首先是插入:

  HashMap的正序插入:


 HashMap<Integer, String>map = new HashMap<Integer, String>();
 long start_map = System.currentTimeMillis();
 for(int i=0;i<MAX;i++){
   map.put(i, String.valueOf(i));
 }
 long map_memory = Runtime.getRuntime().totalMemory();
 long end_map = System.currentTimeMillis()-start_map;
 System.out.println("<---Map的插入时间--->"+end_map+"<---Map占用的内存--->"+map_memory);

 执行后的结果:


 <---Map的插入时间--->914
 <---Map占用的内存--->28598272

SparseArray的正序插入:


 SparseArray<String>sparse = new SparseArray<String>();
 long start_sparse = System.currentTimeMillis();
 for(int i=0;i<MAX;i++){
    sparse.put(i, String.valueOf(i));
 }
 long sparse_memory = Runtime.getRuntime().totalMemory();
 long end_sparse = System.currentTimeMillis()-start_sparse;
 System.out.println("<---Sparse的插入时间--->"+end_sparse+"<---Sparse占用的内存--->"+sparse_memory);
//执行后的结果:
<---Sparse的插入时间--->611
<---Sparse占用的内存--->23281664

   我们可以看到100000条数据量正序插入时SparseArray的效率要比HashMap的效率要高.并且占用的内存也比HashMap要小一些..这里的正序插入表示的是i的值是从小到大进行的一个递增..序列取决于i的值,而不是for循环内部如何执行...

  通过运行后的结果我们可以发现,SparseArray在正序插入的时候,效率要比HashMap要快得多,并且还节省了一部分内存。网上有很多的说法关于二者的效率问题,很多人都会误认为SparseArray要比HashMap的插入和查找的效率要快,还有人则是认为Hash查找当然要比SparseArray中的二分查找要快得多.

  其实我认为Android中在保存<Integer,Value>的时候推荐使用SparseArray的本质目的不是由于效率的原因,而是内存的原因.我们确实看到了插入的时候SparseArray要比HashMap要快.但是这仅仅是正序插入.我们来看看倒序插入的情况.

  HashMap倒序插入:


 System.out.println("<------------- 数据量100000 散列程度小 Map 倒序插入--------------->");
 HashMap<Integer, String>map_2 = new HashMap<Integer, String>();
 long start_map_2 = System.currentTimeMillis();
 for(int i=MAX-1;i>=0;i--){
   map_2.put(MAX-i-1, String.valueOf(MAX-i-1));
 }
 long map_memory_2 = Runtime.getRuntime().totalMemory();
 long end_map_2 = System.currentTimeMillis()-start_map_2;
 System.out.println("<---Map的插入时间--->"+end_map_2+"<---Map占用的内存--->"+map_memory_2);
 //执行后的结果:
 <------------- 数据量100000 Map 倒序插入--------------->
 <---Map的插入时间--->836<---Map占用的内存--->28598272

  SparseArray倒序插入:


System.out.println("<------------- 数据量100000 散列程度小 SparseArray 倒序插入--------------->");
SparseArray<String>sparse_2 = new SparseArray<String>();
long start_sparse_2 = System.currentTimeMillis();
for(int i=MAX-1;i>=0;i--){
  sparse_2.put(i, String.valueOf(MAX-i-1));
}
long sparse_memory_2 = Runtime.getRuntime().totalMemory();
long end_sparse_2 = System.currentTimeMillis()-start_sparse_2;
System.out.println("<---Sparse的插入时间--->"+end_sparse_2+"<---Sparse占用的内存--->"+sparse_memory_2);
//执行后的结果
<------------- 数据量100000 SparseArray 倒序插入--------------->
<---Sparse的插入时间--->20222<---Sparse占用的内存--->23281664

 通过上面的运行结果,我们仍然可以看到,SparseArray与HashMap无论是怎样进行插入,数据量相同时,前者都要比后者要省下一部分内存,但是效率呢?我们可以看到,在倒序插入的时候,SparseArray的插入时间和HashMap的插入时间远远不是一个数量级.由于SparseArray每次在插入的时候都要使用二分查找判断是否有相同的值被插入.因此这种倒序的情况是SparseArray效率最差的时候.

 SparseArray的插入源码我们简单的看一下..


 public void put(int key, E value) {
    int i = ContainerHelpers.binarySearch(mKeys, mSize, key); //二分查找.
    if (i >= 0) { //如果当前这个i在数组中存在,那么表示插入了相同的key值,只需要将value的值进行覆盖..
      mValues[i] = value;
    } else { //如果数组内部不存在的话,那么返回的数值必然是负数.
      i = ~i; //因此需要取i的相反数.
      //i值小于mSize表示在这之前. mKey和mValue数组已经被申请了空间.只是键值被删除了.那么当再次保存新的值的时候.不需要额外的开辟新的内存空间.直接对数组进行赋值即可.
      if (i < mSize && mValues[i] == DELETED) {
        mKeys[i] = key;
        mValues[i] = value;
        return;
      }
      //当需要的空间要超出,但是mKey中存在无用的数值,那么需要调用GC()函数.
      if (mGarbage && mSize >= mKeys.length) {
        gc();
        // Search again because indices may have changed.
        i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
      }
      //如果需要的空间大于了原来申请的控件,那么需要为key和value数组开辟新的空间.
      if (mSize >= mKeys.length) {
        int n = ArrayUtils.idealIntArraySize(mSize + 1);
        //定义了一个新的key和value数组.需要大于mSize
        int[] nkeys = new int[n];
        Object[] nvalues = new Object[n];
        // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
        //对数组进行赋值也就是copy操作.将原来的mKey数组和mValue数组的值赋给新开辟的空间的数组.目的是为了添加新的键值对.
        System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
        System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
        //将数组赋值..这里只是将数组的大小进行扩大..放入键值对的操作不在这里完成.
        mKeys = nkeys;
        mValues = nvalues;
      }
      //如果i的值没有超过mSize的值.只需要扩大mKey的长度即可.
      if (mSize - i != 0) {
        // Log.e("SparseArray", "move " + (mSize - i));
        System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
        System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
      }
      //这里是用来完成放入操作的过程.
      mKeys[i] = key;
      mValues[i] = value;
      mSize++;
    }
  } 

  这就是SparseArray插入函数的源码.每次的插入方式都需要调用二分查找.因此这样在倒序插入的时候会导致情况非常的糟糕,效率上绝对输给了HashMap学过数据结构的大家都知道.Map在插入的时候会对冲突因子做出相应的决策.有非常好的处理冲突的方式.不需要遍历每一个值.因此无论是倒序还是正序插入的效率取决于处理冲突的方式,因此插入时牺牲的时间基本是相同的.

  通过插入.我们还是可以看出二者的差异的.

  我们再来看一下查找首先是HashMap的查找.


 System.out.println("<------------- 数据量100000 Map查找--------------->");
 HashMap<Integer, String>map = new HashMap<Integer, String>();
 for(int i=0;i<MAX;i++){
    map.put(i, String.valueOf(i));
 }
 long start_time =System.currentTimeMillis();
 for(int i=0;i<MAX;i+=100){
      map.get(i);
 }
 long end_time =System.currentTimeMillis()-start_time;
 System.out.println(end_time);
 //执行后的结果
 <!---------查找的时间:175------------>

 SparseArray的查找:


 System.out.println("<------------- 数据量100000 SparseArray 查找--------------->");
 SparseArray<String>sparse = new SparseArray<String>();
 for(int i=0;i<10000;i++){
    sparse.put(i, String.valueOf(i));
 }
 long start_time =System.currentTimeMillis();
 for(int i=0;i<MAX;i+=10){
    sparse.get(i);
 }
 long end_time =System.currentTimeMillis()-start_time;
 System.out.println(end_time);
 //执行后的结果
 <!-----------查找的时间:239---------------->

  我这里也简单的对查找的效率进行了测试.对一个数据或者是几个数据的查询.二者的差异还是非常小的.当数据量是100000条.查100000条的效率还是Map要快一点.数据量为10000的时候.这就差异性就更小.但是Map的查找的效率确实还是赢了一筹.

  其实在我看来.在保存<Integer,E>时使用SparseArray去替换HashMap的主要原因还是因为内存的关系.我们可以看到.保存的数据量无论是大还是小,Map所占用的内存始终是大于SparseArray的.数据量100000条时SparseArray要比HashMap要节约27%的内存.也就是以牺牲效率的代价去节约内存空间.我们知道Android对内存的使用是极为苛刻的.堆区允许使用的最大内存仅仅16M.很容易出现OOM现象的发生.因此在Android中内存的使用是非常的重要的.因此官方才推荐去使用SparseArray<E>去替换HashMap<Integer,E>.官方也确实声明这种差异性不会超过50%.所以牺牲了部分效率换来内存其实在Android中也算是一种很好的选择吧.

您可能感兴趣的文章:Android中ArrayList和数组相互转换详解Android自定义控件属性TypedArray以及attrsAndroid自定义Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)解析Android中string-array数据源的简单使用Android引用arr包的两种方法


--结束END--

本文标题: Android中SparseArray性能优化的使用方法

本文链接: https://www.lsjlt.com/news/25293.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android中SparseArray性能优化的使用方法
    之前一篇文章研究完横向二级菜单,发现其中使用了SparseArray去替换HashMap的使用.于是乎自己查了一些相关资料,自己同时对性能进行了一些测试。首先先说一下Spars...
    99+
    2022-06-06
    方法 优化 Android
  • Android性能优化方法
    GPU过度绘制  •打开开发者选型,“调试GPU过度绘制”,蓝、绿、粉红、红,过度绘制依次加深  •粉红色尽量优化,界面尽量保持蓝绿...
    99+
    2022-06-06
    方法 优化 Android
  • android性能优化的方法有哪些
    Android性能优化的方法有以下几种:1. 代码优化:通过对代码进行优化,包括减少计算量、减少内存占用、减少I/O操作等,提高代码...
    99+
    2023-08-24
    android
  • Android性能优化以及数据优化方法
    Android性能优化-布局优化 今天,继续Android性能优化 一 编码细节优化。 编码细节,对于程序的运行效率也是有很多的影响的。今天这篇主题由于技术能力有限,...
    99+
    2022-06-06
    方法 数据 优化 Android
  • android开发性能优化的方法有哪些
    Android开发性能优化的方法有以下几种:1.减少内存使用:通过使用轻量级数据结构、避免创建不必要的对象、及时释放无用的资源等方式...
    99+
    2023-09-13
    android
  • Nginx性能优化的方法
    这篇文章主要介绍了Nginx性能优化的方法的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Nginx性能优化的方法文章都会有所收获,下面我们一起来看看吧。Linux系统参数优化下文中提到的一些配置,需要较新的Li...
    99+
    2023-06-27
  • .NET使用Collections.Pooled提升性能优化的方法
    目录简介Collections.Pooled如何使用性能对比PooledList<T>PooledDictionary<TKey, TValue>Pooled...
    99+
    2022-11-13
  • MySQL的性能优化方法论
    作者:禅与计算机程序设计艺术 1.简介 一、什么是MySQL? MySQL是一个开源的关系型数据库管理系统,由瑞典MySQL AB开发并发布。它的目的是为了快速、可靠地处理复杂的事务处理,支持多种编...
    99+
    2023-10-18
    大数据 人工智能 语言模型 Java Python 架构设计
  • 性能优化指南:性能优化的一般性原则与方法
    【本文转自博客园 作者:xybaby 原文链接:https://www.cnblogs.com/xybaby/p/9055734.html】作为一个程序员,性能优化是常有的事情,不管是桌面应用还是web应用,不管是前端还是后端,不管是单点应...
    99+
    2023-06-05
  • 浅谈Android开发中ListView控件性能的一些优化方法
    ListView优化一直是一个老生常谈的问题,不管是面试还是平常的开发中,ListView永远不会被忽略掉,那么这篇文章我们来看看如何最大化的优化ListView的性能。 1....
    99+
    2022-06-06
    性能 方法 listview android开发 优化 Android
  • Android性能优化之图片大小,尺寸压缩的方法
    这篇文章主要介绍“Android性能优化之图片大小,尺寸压缩的方法”,在日常操作中,相信很多人在Android性能优化之图片大小,尺寸压缩的方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android性能...
    99+
    2023-06-30
  • Windows8系统优化性能实用方法
    如果你的电脑无法正常响应或无法按预期速度执行任务,你可以尝试 Windows 附带的一些工具来帮助提高性能。     执行可帮助提高性能的任务     “性能信息和工具”的左窗格...
    99+
    2022-06-04
    系统优化 性能 方法
  • PHP中封装性的性能测试与优化方法
    摘要:在PHP开发中,封装性的重要性不言而喻。好的封装性可以提高代码的可读性、维护性和复用性。然而,过于复杂的封装可能会导致性能问题。本文将介绍一些测试和优化方法,帮助你确保封装性和性能的平衡。性能测试工具在进行性能测试之前,我们需要一个可...
    99+
    2023-10-21
    PHP性能测试 性能测试与优化 封装性能优化
  • Vue中的性能优化方案
    目录减少响应式使用1. 使用 computed 缓存计算结果2. 本地化响应式变量3. 函数式组件(Vue2)减少 DOM 渲染压力1. DOM 频繁切换展示的情况使用 v-show...
    99+
    2022-11-13
    Vue 性能优化
  • MySQL性能调优之查询优化的方法
    本篇内容介绍了“MySQL性能调优之查询优化的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、查询慢...
    99+
    2022-10-19
  • react性能优化的方法有哪些
    1. 使用生命周期方法:React的生命周期方法可以帮助我们优化组件的性能。例如,使用shouldComponentUpdate方法...
    99+
    2023-09-13
    react
  • laravel性能优化的方法有哪些
    使用缓存:Laravel提供了多种缓存驱动,如Redis、Memcached等,可以将经常访问的数据进行缓存,减少数据库的查询次数...
    99+
    2023-10-28
    laravel
  • 小程序redux性能优化的方法
    这篇文章主要介绍“小程序redux性能优化的方法”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“小程序redux性能优化的方法”文章能帮助大家解决问题。首先了解小程序的工作原理和性能关键点。1工作原理...
    99+
    2023-06-26
  • Nginx性能优化的方法是什么
    今天小编给大家分享一下Nginx性能优化的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Nginx 性能优化1、N...
    99+
    2023-07-06
  • jQuery性能优化的方法有哪些
    今天小编给大家分享一下jQuery性能优化的方法有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、选择器性能优化建议1...
    99+
    2023-07-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作