热门标签 | 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更好一些。


推荐阅读
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 本文作者分享了在阿里巴巴获得实习offer的经历,包括五轮面试的详细内容和经验总结。其中四轮为技术面试,一轮为HR面试,涵盖了大量的Java技术和项目实践经验。 ... [详细]
  • MATLAB实现n条线段交点计算
    本文介绍了一种通过逐对比较线段来求解交点的简单算法。此外,还提到了一种基于排序的方法,但该方法较为复杂,尚未完全理解。文中详细描述了如何根据线段端点求交点,并判断交点是否在线段上。 ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 作为一名专业的Web前端工程师,掌握HTML和CSS的命名规范是至关重要的。良好的命名习惯不仅有助于提高代码的可读性和维护性,还能促进团队协作。本文将详细介绍Web前端开发中常用的HTML和CSS命名规范,并提供实用的建议。 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • 网易严选Java开发面试:MySQL索引深度解析
    本文详细记录了网易严选Java开发岗位的面试经验,特别针对MySQL索引相关的技术问题进行了深入探讨。通过本文,读者可以了解面试官常问的索引问题及其背后的原理。 ... [详细]
  • 创建项目:Visual Studio Online 入门指南
    本文介绍如何使用微软的 Visual Studio Online(VSO)创建和管理开发项目。作为一款基于云计算的开发平台,VSO 提供了丰富的工具和服务,简化了项目的配置和部署流程。 ... [详细]
  • 探索电路与系统的起源与发展
    本文回顾了电路与系统的发展历程,从电的早期发现到现代电子器件的应用。文章不仅涵盖了基础理论和关键发明,还探讨了这一学科对计算机、人工智能及物联网等领域的深远影响。 ... [详细]
  • 本文详细介绍了 Flink 和 YARN 的交互机制。YARN 是 Hadoop 生态系统中的资源管理组件,类似于 Spark on YARN 的配置方式。我们将基于官方文档,深入探讨如何在 YARN 上部署和运行 Flink 任务。 ... [详细]
  • 2018年3月31日,CSDN、火星财经联合中关村区块链产业联盟等机构举办的2018区块链技术及应用峰会(BTA)核心分会场圆满举行。多位业内顶尖专家深入探讨了区块链的核心技术原理及其在实际业务中的应用。 ... [详细]
  • 在创建新的Android项目时,您可能会遇到aapt错误,提示无法打开libstdc++.so.6共享对象文件。本文将探讨该问题的原因及解决方案。 ... [详细]
  • Python 内存管理机制详解
    本文深入探讨了Python的内存管理机制,涵盖了垃圾回收、引用计数和内存池机制。通过具体示例和专业解释,帮助读者理解Python如何高效地管理和释放内存资源。 ... [详细]
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社区 版权所有