热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

深入分析Android系统中SparseArray的源码

这篇文章主要介绍了深入分析Android系统中SparseArray的源码,SparseArray为Java实现,需要的朋友可以参考下

前言
昨晚想在Android应用中增加一个int映射到String的字典表,使用HashMap实现的时候,Eclipse给出了一个警告,昨晚项目上线紧张,我直接给忽略了,今天看了一下具体的Eclipse提示如下:

  Use new SparseArray (...) instead for better performance 

这个警告的意思是使用SparseArray来替代,以获取更好的性能。

源码
因为SparseArray整体代码比较简单,先把源码展示出来,然后再分析为什么使用SparseArray会比使用HashMap有更好的性能。

   

public class SparseArray implements Cloneable { 
    private static final Object DELETED = new Object(); 
    private boolean mGarbage = false; 
   
    private int[] mKeys; 
    private Object[] mValues; 
    private int mSize; 
   
    /** 
     * Creates a new SparseArray containing no mappings. 
     */ 
    public SparseArray() { 
      this(10); 
    } 
   
    /** 
     * Creates a new SparseArray containing no mappings that will not 
     * require any additional memory allocation to store the specified 
     * number of mappings. If you supply an initial capacity of 0, the 
     * sparse array will be initialized with a light-weight representation 
     * not requiring any additional array allocations. 
     */ 
    public SparseArray(int initialCapacity) { 
      if (initialCapacity == 0) { 
        mKeys = ContainerHelpers.EMPTY_INTS; 
        mValues = ContainerHelpers.EMPTY_OBJECTS; 
      } else { 
        initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); 
        mKeys = new int[initialCapacity]; 
        mValues = new Object[initialCapacity]; 
      } 
      mSize = 0; 
    } 
   
    @Override 
    @SuppressWarnings("unchecked") 
    public SparseArray clone() { 
      SparseArray clOne= null; 
      try { 
        clOne= (SparseArray) super.clone(); 
        clone.mKeys = mKeys.clone(); 
        clone.mValues = mValues.clone(); 
      } catch (CloneNotSupportedException cnse) { 
        /* ignore */ 
      } 
      return clone; 
    } 
   
    /** 
     * Gets the Object mapped from the specified key, or null 
     * if no such mapping has been made. 
     */ 
    public E get(int key) { 
      return get(key, null); 
    } 
   
    /** 
     * Gets the Object mapped from the specified key, or the specified Object 
     * if no such mapping has been made. 
     */ 
    @SuppressWarnings("unchecked") 
    public E get(int key, E valueIfKeyNotFound) { 
      int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 
   
      if (i <0 || mValues[i] == DELETED) { 
        return valueIfKeyNotFound; 
      } else { 
        return (E) mValues[i]; 
      } 
    } 
   
    /** 
     * Removes the mapping from the specified key, if there was any. 
     */ 
    public void delete(int key) { 
      int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 
   
      if (i >= 0) { 
        if (mValues[i] != DELETED) { 
          mValues[i] = DELETED; 
          mGarbage = true; 
        } 
      } 
    } 
   
    /** 
     * Alias for {@link #delete(int)}. 
     */ 
    public void remove(int key) { 
      delete(key); 
    } 
   
    /** 
     * Removes the mapping at the specified index. 
     */ 
    public void removeAt(int index) { 
      if (mValues[index] != DELETED) { 
        mValues[index] = DELETED; 
        mGarbage = true; 
      } 
    } 
   
    /** 
     * Remove a range of mappings as a batch. 
     * 
     * @param index Index to begin at 
     * @param size Number of mappings to remove 
     */ 
    public void removeAtRange(int index, int size) { 
      final int end = Math.min(mSize, index + size); 
      for (int i = index; i = 0) { 
        mValues[i] = value; 
      } else { 
        i = ~i; 
   
        if (i = mKeys.length) { 
          gc(); 
   
          // Search again because indices may have changed. 
          i = ~ContainerHelpers.binarySearch(mKeys, mSize, key); 
        } 
   
        if (mSize >= mKeys.length) { 
          int n = ArrayUtils.idealIntArraySize(mSize + 1); 
   
          int[] nkeys = new int[n]; 
          Object[] nvalues = new Object[n]; 
   
          // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); 
          System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); 
          System.arraycopy(mValues, 0, nvalues, 0, mValues.length); 
   
          mKeys = nkeys; 
          mValues = nvalues; 
        } 
   
        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++; 
      } 
    } 
   
    /** 
     * Returns the number of key-value mappings that this SparseArray 
     * currently stores. 
     */ 
    public int size() { 
      if (mGarbage) { 
        gc(); 
      } 
   
      return mSize; 
    } 
   
    /** 
     * Given an index in the range 0...size()-1, returns 
     * the key from the indexth key-value mapping that this 
     * SparseArray stores. 
     * 
     * 

The keys corresponding to indices in ascending order are guaranteed to * be in ascending order, e.g., keyAt(0) will return the * smallest key and keyAt(size()-1) will return the largest * key.

*/ public int keyAt(int index) { if (mGarbage) { gc(); } return mKeys[index]; } /** * Given an index in the range 0...size()-1, returns * the value from the indexth key-value mapping that this * SparseArray stores. * *

The values corresponding to indices in ascending order are guaranteed * to be associated with keys in ascending order, e.g., * valueAt(0) will return the value associated with the * smallest key and valueAt(size()-1) will return the value * associated with the largest key.

*/ @SuppressWarnings("unchecked") public E valueAt(int index) { if (mGarbage) { gc(); } return (E) mValues[index]; } /** * Given an index in the range 0...size()-1, sets a new * value for the indexth key-value mapping that this * SparseArray stores. */ public void setValueAt(int index, E value) { if (mGarbage) { gc(); } mValues[index] = value; } /** * Returns the index for which {@link #keyAt} would return the * specified key, or a negative number if the specified * key is not mapped. */ public int indexOfKey(int key) { if (mGarbage) { gc(); } return ContainerHelpers.binarySearch(mKeys, mSize, key); } /** * Returns an index for which {@link #valueAt} would return the * specified key, or a negative number if no keys map to the * specified value. *

Beware that this is a linear search, unlike lookups by key, * and that multiple keys can map to the same value and this will * find only one of them. *

Note also that unlike most collections' {@code indexOf} methods, * this method compares values using {@code ==} rather than {@code equals}. */ public int indexOfValue(E value) { if (mGarbage) { gc(); } for (int i = 0; i = mKeys.length) { gc(); } int pos = mSize; if (pos >= mKeys.length) { int n = ArrayUtils.idealIntArraySize(pos + 1); int[] nkeys = new int[n]; Object[] nvalues = new Object[n]; // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); System.arraycopy(mValues, 0, nvalues, 0, mValues.length); mKeys = nkeys; mValues = nvalues; } mKeys[pos] = key; mValues[pos] = value; mSize = pos + 1; } /** * {@inheritDoc} * *

This implementation composes a string by iterating over its mappings. If * this map contains itself as a value, the string "(this Map)" * will appear in its place. */ @Override public String toString() { if (size() <= 0) { return "{}"; } StringBuilder buffer = new StringBuilder(mSize * 28); buffer.append('{'); for (int i=0; i 0) { buffer.append(", "); } int key = keyAt(i); buffer.append(key); buffer.append('='); Object value = valueAt(i); if (value != this) { buffer.append(value); } else { buffer.append("(this Map)"); } } buffer.append('}'); return buffer.toString(); } }


首先,看一下SparseArray的构造函数:

   

 /** 
   * Creates a new SparseArray containing no mappings. 
   */ 
  public SparseArray() { 
    this(10); 
  } 
   
  /** 
   * Creates a new SparseArray containing no mappings that will not 
   * require any additional memory allocation to store the specified 
   * number of mappings. If you supply an initial capacity of 0, the 
   * sparse array will be initialized with a light-weight representation 
   * not requiring any additional array allocations. 
   */ 
  public SparseArray(int initialCapacity) { 
    if (initialCapacity == 0) { 
      mKeys = ContainerHelpers.EMPTY_INTS; 
      mValues = ContainerHelpers.EMPTY_OBJECTS; 
    } else { 
      initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); 
      mKeys = new int[initialCapacity]; 
      mValues = new Object[initialCapacity]; 
    } 
    mSize = 0; 
  } 

从构造方法可以看出,这里也是预先设置了容器的大小,默认大小为10。

再来看一下添加数据操作:

  /** 
   * Adds a mapping from the specified key to the specified value, 
   * replacing the previous mapping from the specified key if there 
   * was one. 
   */ 
  public void put(int key, E value) { 
    int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 
   
    if (i >= 0) { 
      mValues[i] = value; 
    } else { 
      i = ~i; 
   
      if (i = mKeys.length) { 
        gc(); 
   
        // Search again because indices may have changed. 
        i = ~ContainerHelpers.binarySearch(mKeys, mSize, key); 
      } 
   
      if (mSize >= mKeys.length) { 
        int n = ArrayUtils.idealIntArraySize(mSize + 1); 
   
        int[] nkeys = new int[n]; 
        Object[] nvalues = new Object[n]; 
   
        // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); 
        System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); 
        System.arraycopy(mValues, 0, nvalues, 0, mValues.length); 
   
        mKeys = nkeys; 
        mValues = nvalues; 
      } 
   
      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++; 
    } 
  } 


再看查数据的方法:

  /** 
   * Gets the Object mapped from the specified key, or null 
   * if no such mapping has been made. 
   */ 
  public E get(int key) { 
    return get(key, null); 
  } 
   
  /** 
   * Gets the Object mapped from the specified key, or the specified Object 
   * if no such mapping has been made. 
   */ 
  @SuppressWarnings("unchecked") 
  public E get(int key, E valueIfKeyNotFound) { 
    int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 
   
    if (i <0 || mValues[i] == DELETED) { 
      return valueIfKeyNotFound; 
    } else { 
      return (E) mValues[i]; 
    } 
  } 


可以看到,在put数据和get数据的过程中,都统一调用了一个二分查找算法,其实这也就是SparseArray能够提升效率的核心。

   

 static int binarySearch(int[] array, int size, int value) { 
    int lo = 0; 
    int hi = size - 1; 
   
    while (lo <= hi) { 
      final int mid = (lo + hi) >>> 1; 
      final int midVal = array[mid]; 
   
      if (midVal  value) { 
        hi = mid - 1; 
      } else { 
        return mid; // value found 
      } 
    } 
    return ~lo; // value not present 
  } 

个人认为(lo + hi) >>> 1的方法有些怪异,直接用 lo + (hi - lo) / 2更好一些。


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了Java的集合及其实现类,包括数据结构、抽象类和具体实现类的关系,详细介绍了List接口及其实现类ArrayList的基本操作和特点。文章通过提供相关参考文档和链接,帮助读者更好地理解和使用Java的集合类。 ... [详细]
  • 本文介绍了一种图片处理应用,通过固定容器来实现缩略图的功能。该方法可以实现等比例缩略、扩容填充和裁剪等操作。详细的实现步骤和代码示例在正文中给出。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 本文介绍了在Ubuntu 11.10 x64环境下安装Android开发环境的步骤,并提供了解决常见问题的方法。其中包括安装Eclipse的ADT插件、解决缺少GEF插件的问题以及解决无法找到'userdata.img'文件的问题。此外,还提供了相关插件和系统镜像的下载链接。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • C++语言入门:数组的基本知识和应用领域
    本文介绍了C++语言的基本知识和应用领域,包括C++语言与Python语言的区别、C++语言的结构化特点、关键字和控制语句的使用、运算符的种类和表达式的灵活性、各种数据类型的运算以及指针概念的引入。同时,还探讨了C++语言在代码效率方面的优势和与汇编语言的比较。对于想要学习C++语言的初学者来说,本文提供了一个简洁而全面的入门指南。 ... [详细]
  • 本文介绍了Python函数的定义与调用的方法,以及函数的作用,包括增强代码的可读性和重用性。文章详细解释了函数的定义与调用的语法和规则,以及函数的参数和返回值的用法。同时,还介绍了函数返回值的多种情况和多个值的返回方式。通过学习本文,读者可以更好地理解和使用Python函数,提高代码的可读性和重用性。 ... [详细]
  • 本文介绍了Cocos2dx学习笔记中的更新函数scheduleUpdate、进度计时器CCProgressTo和滚动视图CCScrollView的用法。详细介绍了scheduleUpdate函数的作用和使用方法,以及schedule函数的区别。同时,还提供了相关的代码示例。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
author-avatar
淀死鈊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有