性能优化是我们做开发的必须要熟练掌握的技能,所以我打算写一个性能优化专题,把平时用到的一些优化方法记录下来,以便忘记的时候可以快速查找,同时也给给其他开发者提供微薄之力吧:

这篇文章讲述的是在一些特定的场景使用使用ArrayMap与SparseArray代替HashMap,提高对数据的操作;
先看看官方文档的描述:

ArrayMap is a generic key->value mapping data structure that is designed to be more memory efficient than a traditional HashMap, this implementation is a version of the platform's ArrayMap that can be used on older versions of the platform.SparseArrays map integers to Objects. Unlike a normal array of Objects, there can be gaps in the indices. It is intended to be more memory efficient than using a HashMap to map Integers to Objects, both because it avoids auto-boxing keys and its data structure doesn't rely on an extra entry object for each mapping.Note that for containers holding up to hundreds of items, the performance difference is not significant, less than 50%.
  • 1

从官方文档可以看出使用ArrayMap与SparseArray都要比传统的HashMap 更有效率;但是最后有一个note,也就是当数据量达到千级以上的时候,ArrayMap与SparseArray都要比传统的HashMap 效率更低50%;

HashMap
HashMap允许使用 null 值和 null 键,是基于hashing原理,我们通过put()和get()方法储存和获取对象。
HashMap的结构:

HashMap 有两个参数影响其性能:初始容量 和加载因子。容量是哈希表中桶的数量(默认16组),初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。加载因子默认值为0.75 。
当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用LinkedList来解决碰撞问题,当发生碰撞了,对象将会储存在LinkedList的下一个节点中。 HashMap在每个LinkedList节点中储存键值对对象。
put()方法

 @Override public V put(K key, V value) {if (key == null) {//允许空值return putValueForNullKey(value);}//使得hash过后的值的分布更加均匀,尽可能地避免冲突int hash = Collections.secondaryHash(key);HashMapEntry<K, V>[] tab = table;//当tab.length为2^n-1的时候,可以保证结果不大于tab.lengthint index = hash & (tab.length - 1);for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {if (e.hash == hash && key.equals(e.key)) {preModify(e);V oldValue = e.value;e.value = value;return oldValue;}}// No entry for (non-null) key is present; create onemodCount++;//扩容if (size++ > threshold) {tab = doubleCapacity();index = hash & (tab.length - 1);}addNewEntry(key, value, hash, index);return null;}
  • 1

当两个Key同时hash到一个值时,就会出现这样的冲突。这个冲突主要有2种解决方法。

1、开放地址,亦即如果hash冲突,则在空闲的位置进行插入
2、hash复用,同一个hash值,链式地加入多个value
  • 1

get()方法

  public V get(Object key) {if (key == null) {//允许value为空HashMapEntry<K, V> e = entryForNullKey;return e == null ? null : e.value;}//使得hash过后的值的分布更加均匀,尽可能地避免冲突int hash = Collections.secondaryHash(key);HashMapEntry<K, V>[] tab = table;for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];e != null; e = e.next) {K eKey = e.key;if (eKey == key || (e.hash == hash && key.equals(eKey))) {return e.value;}}return null;}
  • 1

HashMap还有很多方法,这里就不一一例举了;

通过get与put的源码,可以看出HashMap获取数据是通过遍历Entry[]数组来得到对应的元素,在数据量很大时候会比较慢,所以在Android中,HashMap是比较费内存的,我们在一些情况下可以使用SparseArray和ArrayMap来代替HashMap。
SparseArray

/*** A copy of the current platform (currently {@link android.os.Build.VERSION_CODES#KITKAT}* version of {@link android.util.SparseArray}; provides a removeAt() method and other things.*/
public class SparseArrayCompat<E> implements Cloneable {private static final Object DELETED = new Object();private boolean mGarbage = false;private int[] mKeys;private Object[] mValues;private int mSize;
  • 1

可以看出SparseArray由两个数组mKeys和mValues存放数据;其中key的类型为int型,这就显得SparseArray比HashMap更省内存一些,SparseArray在存储和读取数据时候,使用的是二分查找法,那何为二分法呢?
先看一下put()方法:

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 < mSize && mValues[i] == DELETED) {mKeys[i] = key;mValues[i] = value;return;}if (mGarbage && mSize >= mKeys.length) {gc();// Search again because indices may have changed.i = ~ ContainerHelpers.binarySearch(mKeys, mSize, key);}//如果容量不够 if (mSize >= mKeys.length) {int n =  ContainerHelpers.idealIntArraySize(mSize + 1);int[] nkeys = new int[n];Object[] nvalues = new Object[n];// Log.e("SparseArray", "grow " + mKeys.length + " to " + n);//特别注意这,是copy,而不是new,效率提升 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++;}}
  • 1

在上面代码中有一个ContainerHelpers.binarySearch(mKeys, mSize, key)方法,这是Arrays提供了一个方便查询的方法,也就是我们所说的“二分法”;

  // This is Arrays.binarySearch(), but doesn't do any argument validation.static int binarySearch(int[] array, int size, int value) {int lo = 0;int hi = size - 1;while (lo <= hi) {int mid = (lo + hi) >>> 1;//无符号右移 int midVal = array[mid];if (midVal < value) {lo = mid + 1;} else if (midVal > value) {hi = mid - 1;} else {return mid;  // value found}}return ~lo;  // value not present}
  • 1

get()方法:

 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];}}
  • 1

使用二分查找法和之前的key比较当前我们添加的元素的key的大小,然后按照从小到大的顺序排列好,所以,SparseArray存储的元素都是按元素的key值从小到大排列好的。
而在获取数据的时候,也是使用二分查找法判断元素的位置,所以,在获取数据的时候非常快,比HashMap快的多,因为HashMap获取数据是通过遍历Entry[]数组来得到对应的元素。
其他的一些方法:
转存失败重新上传取消
ArrayMap
ArrayMap是一个键值对映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值,它和SparseArray一样,也会对key使用二分法进行从小到大排序,区别是ArrayMap的key是hash值,

public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V> {MapCollections<K, V> mCollections;public ArrayMap() {super();}
  • 1

可以看出ArrayMap的构造方法直接调用的父类的构造方法:

    int[] mHashes;Object[] mArray;public SimpleArrayMap() {mHashes = ContainerHelpers.EMPTY_INTS;mArray = ContainerHelpers.EMPTY_OBJECTS;mSize = 0;}
  • 1

put()方法:

  public V put(K key, V value) {final int hash;int index;if (key == null) {hash = 0;index = indexOfNull();} else {hash = key.hashCode();index = indexOf(key, hash);}if (index >= 0) {index = (index<<1) + 1;final V old = (V)mArray[index];mArray[index] = value;return old;}index = ~index;if (mSize >= mHashes.length) {final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1)): (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);final int[] ohashes = mHashes;final Object[] oarray = mArray;allocArrays(n);if (mHashes.length > 0) {if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);System.arraycopy(oarray, 0, mArray, 0, oarray.length);}//释放资源freeArrays(ohashes, oarray, mSize);}if (index < mSize) {if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)+ " to " + (index+1));System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);}mHashes[index] = hash;mArray[index<<1] = key;mArray[(index<<1)+1] = value;mSize++;return null;}

安卓性能优化—使用ArrayMap与SparseArray相关推荐

  1. 安卓性能优化之启动优化

    安卓性能优化之启动优化 两个定律 2-5-8原则 八秒定律 启动方式 冷启动 热启动 温启动 启动耗时统计 系统日志 adb命令 启动耗时分析 CPU Profile 工具介绍 使用方式 数据分析 C ...

  2. Android中HashMap内存优化之ArrayMap和SparseArray

    ArrayMap及SparseArray是android的系统API,是专门为移动设备而定制的.用于在一定情况下取代HashMap而达到节省内存的目的. 在Android开发中HashMap使用频率相 ...

  3. 安卓性能优化(响应优化)

    姊妹篇:性能优化(内存优化) 安卓app响应速度或使用流畅度是衡量性能的一个指标.如果一个应用用户启动应用时缓慢.使用时卡顿.甚至出现ANR那是很糟糕的体验. 通常,当应用无法响应用户输入时,系统即会 ...

  4. 安卓性能优化——提高编码性能的技巧

    引用了大量API文档的内容,没有对超链接进行删改,有需要的朋友可以直接点超链接访问API文档. 上一篇关于主要内存的性能优化,这一篇主要关于编码. Performance tips This docu ...

  5. 精通安卓性能优化-第五章(三)

    Concurrency 在java.util.concurrent.atomic和java.util.concurrent.locks包中定义了更多的类.java.util.concurrent.at ...

  6. 安卓性能优化面试,从草根到百万年薪程序员的十年风雨之路

    前言 先简单交代一下背景吧,本人毕业于某不知名985的本硕,17年毕业加入阿里,今年下半年跳槽到了头条,一直从事后端研发相关的工作. 之前没有实习经历,算是两年半的工作经验吧.这两年半之间完成了一次晋 ...

  7. 安卓性能优化(3)异常处理应用瘦身

    简介 异常处理 ANR 原因 方案 OOM 原因 优化 其他优化 线程优化 Service使用优化 应用瘦身 总结 简介 常见的异常无非就是ANR和OOM,掌握如何避免这两种异常有助于提升应用性能.另 ...

  8. 安卓性能优化之内存优化

    Java对象生命周期: 创建:为对象分配内存空间,构造对象 应用:此时 对象至少被一个强引用持有 不可见:未被任何强引用持有,进行可达性分析 不可达:可达性分析为不可达,进入下一阶段 收集:当垃圾回收 ...

  9. 安卓性能优化之界面UI优化——ViewStub

    ViewStub是一个轻量级的视图控件,而实际开发中在合适的场景中使用,可以提高渲染速度,占用的内存更少,从而提高App的UI性能. 理解ViewStub这个控件,首先要清楚它的几条属性和应用条件: ...

最新文章

  1. java静态多态_Java静态方法不具有多态性详解
  2. 160 - 48 DueList.3
  3. dexpress 流程图_DevExpress控件使用经验总结
  4. Java多线程面试题与答案
  5. 【Flink】Flink 单个任务 多个流的消费同一个topic的时候其中一个流卡死 不消费
  6. stream 多个字段分组_Python Pandas对Excel数据的分组聚合和数据透视
  7. Oracle序列号详解
  8. android 横屏finish 会变竖屏,[android] Activity 的生命周期 以及 横屏竖屏切换时 Activity 的状态变化...
  9. mysql维护计划 自动备份_MysQL windows 下自动备份,同时加入计划任务脚本
  10. 生长区域算法的php实现
  11. linux思源黑体乱码,Source Han Sans 思源黑体字体包
  12. CAM是利用计算机,利用DroidCam将手机摄像头打造成计算机摄像头
  13. 蛋白质组学数据分析在生物医学领域的应用
  14. 保温杯内壁有很多小泡泡
  15. 将军赶路不追小兔—看完纪录片《百万富豪谷底翻身》
  16. sjtu1313 太湖旅行
  17. 假如用VIVO手机拍照,这几个功能一定要打开,效果媲美单反
  18. 基于python OpenCV多边形图像识别的实现
  19. 视觉SLAM:一直在入门,从未到精通
  20. python3 psycopg2查询PostgreSQL返回字典

热门文章

  1. 【服装设计作品】 打开“重生”之门,唤起人类意识
  2. 读书笔记-精准努力-说话直就是情商低
  3. chrome浏览器安装失败,已解决(方便)
  4. android 模拟器 2.0,Android Studio 2.0 之 新的模拟器
  5. vue实现百度语音播报API调用
  6. DOCTYPE作用及含义
  7. 最精简mysql查询
  8. Python基础知识思维导图
  9. 浙江计算机二级文化程度,浙江省计算机二级办公软件高级应用技术Excel解答_RANKvLOOKUP效果及步骤...
  10. gitlab配置域名访问