高效程序有两个重要指标:速度,内存,移动app中内存比重要多一些,为此在速度相差不是很大的时候,优先考虑内存,container是一个重要部分,对此google对一些原java容器设计新的容器进行替换Map结构。在写程序时使用Map类大部份情况都会用到,尤其是HashMap使用频率相当高,使用HashMap会涉及一个要求key与value必须为对象类型,

而不能为基本类型,这就导致了本可以基本类型的数据必须转换为其对象包装类型(int->Integer,long->Long......)这就涉及到需要占用更多内存以及拆箱装箱频繁转换问题。

例如:Map<Integer,Integer>,Integer占用内存>12

这里涉及一个引用类型计算问题,引用java规范;

在Java中,一个空Object对象的大小是8byte,这个大小只是保存堆中一个没有任何属性的对象的大小。

看下面语句:

Object ob = new Object();

这样在程序中完成了一个Java对象的生命,但是它所占的空间为:4byte+8byte。4byte是Java栈中保存引用的所需要的空间。而那8byte则是Java堆中对象的信息。

因为所有的Java非基本类型的对象都需要默认继承Object对象,因此不论什么样的Java对象,其大小都必须是大于8byte。

包装类型已经成为对象了,因此需要把他们作为对象来看待。包装类型的大小至少是12byte(声明一个空Object至少需要的空间),而且12byte没有包含任何有效信息,同时,因为Java对象分配内存时其大小是8的整数倍,因此一个基本类型包装类的大小至少是16byte。这个内存占用是很恐怖的,它是使用基本类型的N倍(N>2),有些类型的内存占用更是夸张(随便想下就知道了)。因此,可能的话应尽量少使用包装类。

为此google专门设计了当key为基本类型时的替换Map数据结构容器,在android.util包下,根据文档介绍如下

SparseArray -> map integer to Object
SparseBooleanArrays -> map integers to booleans
SparseIntArrays -> map integers to integers
SparseLongArrays -> map integers to longs
LongSparseArray -> map longs to Objects

可以看出key为integer时情况较多,原理是key存储在int[] mKeys数组内,value存储在对应的(int,long,object)[] mValues数组内,采用二分法计算索引位置

SparseArray实现

  1. public class SparseArray<E> implements Cloneable {
  2. private static final Object DELETED = new Object();
  3. private boolean mGarbage = false;
  4.  
  5. private int[] mKeys;//使用array存储int key
  6. private Object[] mValues;//使用array存储泛型Value
  7. private int mSize;
  8.  
  9. /**
  10. * Creates a new SparseArray containing no mappings.
  11. */
  12. public SparseArray() {
  13. this(10);//默认容量10
  14. }
  15.  
  16. public SparseArray(int initialCapacity) {
  17. if (initialCapacity == 0) {
  18. mKeys = EmptyArray.INT;
  19. mValues = EmptyArray.OBJECT;
  20. } else {
  21. mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity);
  22. mKeys = new int[mValues.length];
  23. }
  24. mSize = 0;
  25. }
  26.  
  27. public E get(int key) {
  28. return get(key, null);
  29. }
  30.  
  31. @SuppressWarnings("unchecked")
  32. public E get(int key, E valueIfKeyNotFound) {
  33. int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//使用二分查找查找key
  34.  
  35. if (i < 0 || mValues[i] == DELETED) {//没有找到key或者Value已经被deleted
  36. return valueIfKeyNotFound;
  37. } else {
  38. return (E) mValues[i];
  39. }
  40. }
  41.  
  42. public void put(int key, E value) {
  43. int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//二分查找key是否存在,如果没有找到时,这里返回的是-low,也就是待插入位置取反
  44. if (i >= 0) {//存在直接替换value
  45. mValues[i] = value;
  46. } else {
  47. i = ~i;//待插入位置
  48.  
  49. if (i < mSize && mValues[i] == DELETED) {//pos在size范围内,并且该pos被deleted直接赋值
  50. mKeys[i] = key;
  51. mValues[i] = value;
  52. return;
  53. }
  54.  
  55. if (mGarbage && mSize >= mKeys.length) {//是否需要回收处理
  56. gc();
  57.  
  58. // Search again because indices may have changed.
  59. i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
  60. }
  61.  
  62. mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);//扩容key处理
  63. mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);//扩容value处理
  64. mSize++;
  65. }
  66. }
  67. ......

这里注意到一个Deleted标识,这个是什么呢?就是前面类的一个object成员变量,当执行delete,remove时value[i]被标记

  1. public void delete(int key) {
  2. int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
  3.  
  4. if (i >= 0) {
  5. if (mValues[i] != DELETED) {
  6. mValues[i] = DELETED;
  7. mGarbage = true;
  8. }
  9. }
  10. }
  11.  
  12. public void removeAt(int index) {
  13. if (mValues[index] != DELETED) {
  14. mValues[index] = DELETED;
  15. mGarbage = true;
  16. }
  17. }

这样标记的一个好处就是避免array频繁移动元素,对数据频繁的delete,removed,put操作时,在一定程度上可以提供效率,只有当执行gc时才进行回收处理

  1. private void gc() {
  2. int n = mSize;
  3. int o = 0;
  4. int[] keys = mKeys;
  5. Object[] values = mValues;
  6.  
  7. for (int i = 0; i < n; i++) {
  8. Object val = values[i];
  9.  
  10. if (val != DELETED) {
  11. if (i != o) {
  12. keys[o] = keys[i];
  13. values[o] = val;
  14. values[i] = null;
  15. }
  16. o++;
  17. }
  18. }
  19.  
  20. mGarbage = false;
  21. mSize = o;
  22.  
  23. // Log.e("SparseArray", "gc end with " + mSize);
  24. }

对于常用的基本类型google已经有对应实现类,例如:SparseLongArray,SparseIntArray,SparseBooleanArray

对于key不是Integer,Long的基本类型情况,在api 19时可以使用ArrayMap,原理:与SparseArray类似处理,hash值存储在int []mHashes,value存储在Object[] mArray中,

ArrayMap实现:

  1. public final class ArrayMap<K, V> implements Map<K, V> {
  2. ...
  3.  
  4. /**
  5. * @hide Special immutable empty ArrayMap.
  6. */
  7. public static final ArrayMap EMPTY = new ArrayMap(true);
  8. ...
  9.  
  10. /**
  11. * Special hash array value that indicates the container is immutable.
  12. */
  13. static final int[] EMPTY_IMMUTABLE_INTS = new int[0];
  14.  
  15. int[] mHashes;//存储key的hashCode
  16. Object[] mArray;//存储key(偶数索引存储key)与value(奇数索引存储value)
  17. int mSize;
  18.  
  19. int indexOf(Object key, int hash) {//查找hash code索引位置
  20. final int N = mSize;
  21.  
  22. // Important fast case: if nothing is in here, nothing to look for.
  23. if (N == 0) {
  24. return ~0;
  25. }
  26.  
  27. int index = ContainerHelpers.binarySearch(mHashes, N, hash);//二分查找hash code的index
  28.  
  29. // If the hash code wasn't found, then we have no entry for this key.
  30. if (index < 0) {//未查找到
  31. return index;
  32. }
  33.  
  34. // If the key at the returned index matches, that's what we want.
  35. if (key.equals(mArray[index<<1])) {//查找到index,对应到mArray位置中指定的key index
  36. return index;
  37. }
  38.  
  39. // Search for a matching key after the index.
  40. int end;
  41. for (end = index + 1; end < N && mHashes[end] == hash; end++) {
  42. if (key.equals(mArray[end << 1])) return end;
  43. }
  44.  
  45. // Search for a matching key before the index.
  46. for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
  47. if (key.equals(mArray[i << 1])) return i;
  48. }
  49.  
  50. return ~end;
  51. }
  52.  
  53. private void allocArrays(final int size) {
  54. if (mHashes == EMPTY_IMMUTABLE_INTS) {
  55. throw new UnsupportedOperationException("ArrayMap is immutable");
  56. }
  57. ......
  58. mHashes = new int[size];//指定hash array size
  59. mArray = new Object[size<<1];//mArray大小为size x2,因为这里使用一个array即存储key,又存储value
  60. }
  61.  
  62. public void ensureCapacity(int minimumCapacity) {//容量不足时扩容处理
  63. if (mHashes.length < minimumCapacity) {
  64. final int[] ohashes = mHashes;
  65. final Object[] oarray = mArray;
  66. allocArrays(minimumCapacity);
  67. if (mSize > 0) {
  68. System.arraycopy(ohashes, 0, mHashes, 0, mSize);
  69. System.arraycopy(oarray, 0, mArray, 0, mSize<<1);
  70. }
  71. freeArrays(ohashes, oarray, mSize);
  72. }
  73. }
  74.  
  75. @Override
  76. public boolean containsKey(Object key) {
  77. return indexOfKey(key) >= 0;
  78. }
  79.  
  80. public int indexOfKey(Object key) {
  81. return key == null ? indexOfNull() : indexOf(key, key.hashCode());
  82. }
  83.  
  84. int indexOfValue(Object value) {//查找指定value的索引位置
  85. final int N = mSize*2;
  86. final Object[] array = mArray;
  87. if (value == null) {//null分开查找,value存储在奇数位置,每次+2跳步
  88. for (int i=1; i<N; i+=2) {
  89. if (array[i] == null) {
  90. return i>>1;
  91. }
  92. }
  93. } else {
  94. for (int i=1; i<N; i+=2) {
  95. if (value.equals(array[i])) {
  96. return i>>1;
  97. }
  98. }
  99. }
  100. return -1;
  101. }
  102.  
  103. @Override
  104. public boolean containsValue(Object value) {
  105. return indexOfValue(value) >= 0;
  106. }
  107.  
  108. @Override
  109. public V get(Object key) {
  110. final int index = indexOfKey(key);
  111. return index >= 0 ? (V)mArray[(index<<1)+1] : null;
  112. }
  113.  
  114. @Override
  115. public V remove(Object key) {
  116. final int index = indexOfKey(key);
  117. if (index >= 0) {
  118. return removeAt(index);
  119. }
  120.  
  121. return null;
  122. }
  123.  
  124. public V removeAt(int index) {
  125. final Object old = mArray[(index << 1) + 1];
  126. if (mSize <= 1) {
  127. // Now empty.
  128. if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
  129. freeArrays(mHashes, mArray, mSize);
  130. mHashes = EmptyArray.INT;
  131. mArray = EmptyArray.OBJECT;
  132. mSize = 0;
  133. } else {
  134. if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {//hash array长度>预定baseSize 2倍,元素个数小于3分之一时,进行容量缩减处理
  135. // Shrunk enough to reduce size of arrays. We don't allow it to//减少内存占用,提供使用效率
  136. // shrink smaller than (BASE_SIZE*2) to avoid flapping between
  137. // that and BASE_SIZE.
  138. final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);
  139.  
  140. if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
  141.  
  142. final int[] ohashes = mHashes;
  143. final Object[] oarray = mArray;
  144. allocArrays(n);
  145.  
  146. mSize--;
  147. if (index > 0) {
  148. if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
  149. System.arraycopy(ohashes, 0, mHashes, 0, index);
  150. System.arraycopy(oarray, 0, mArray, 0, index << 1);
  151. }
  152. if (index < mSize) {
  153. if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize
  154. + " to " + index);
  155. System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
  156. System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,
  157. (mSize - index) << 1);
  158. }
  159. } else {
  160. mSize--;
  161. if (index < mSize) {
  162. if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize
  163. + " to " + index);
  164. System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
  165. System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,
  166. (mSize - index) << 1);
  167. }
  168. mArray[mSize << 1] = null;
  169. mArray[(mSize << 1) + 1] = null;
  170. }
  171. }
  172. return (V)old;
  173. }
  174.  
  175. @Override
  176. public V put(K key, V value) {
  177. final int hash;
  178. int index;//根据key为null,不为null两种方式查找index
  179. if (key == null) {
  180. hash = 0;
  181. index = indexOfNull();
  182. } else {
  183. hash = key.hashCode();
  184. index = indexOf(key, hash);
  185. }
  186. if (index >= 0) {//查找到已有key,则替换新值,返回旧值
  187. index = (index<<1) + 1;
  188. final V old = (V)mArray[index];
  189. mArray[index] = value;
  190. return old;
  191. }
  192.  
  193. index = ~index;//等到插入位置
  194. if (mSize >= mHashes.length) {//扩展array大小
  195. final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))
  196. : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
  197.  
  198. if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);
  199.  
  200. final int[] ohashes = mHashes;
  201. final Object[] oarray = mArray;
  202. allocArrays(n);
  203.  
  204. if (mHashes.length > 0) {
  205. if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");
  206. System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
  207. System.arraycopy(oarray, 0, mArray, 0, oarray.length);
  208. }
  209.  
  210. freeArrays(ohashes, oarray, mSize);
  211. }
  212.  
  213. if (index < mSize) {//移位腾出指定位置空间,待插入位置
  214. if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)
  215. + " to " + (index+1));
  216. System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
  217. System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
  218. }
  219.  
  220. mHashes[index] = hash;
  221. mArray[index<<1] = key;
  222. mArray[(index<<1)+1] = value;
  223. mSize++;
  224. return null;
  225. }
  226.  
  227. /**
  228. * Special fast path for appending items to the end of the array without validation.
  229. * The array must already be large enough to contain the item.
  230. * @hide
  231. */
  232. public void append(K key, V value) {//快速插入指定key-value,当array容量够大,元素较少时使用,去掉了扩容,处理使用抛异常替代
  233. int index = mSize;//在最后一个元素位置后执行添加
  234. final int hash = key == null ? 0 : key.hashCode();
  235. if (index >= mHashes.length) {//hash array边界检测
  236. throw new IllegalStateException("Array is full");
  237. }
  238. if (index > 0 && mHashes[index-1] > hash) {
  239. //hash array采用升序排序存储,即前面hash code <后面元素,当插入元素<最后元素时,说明需要进行元素移动
  240. RuntimeException e = new RuntimeException("here");
  241. e.fillInStackTrace();
  242. Log.w(TAG, "New hash " + hash
  243. + " is before end of array hash " + mHashes[index-1]
  244. + " at index " + index + " key " + key, e);
  245. put(key, value);//执行移动元素
  246. return;
  247. }
  248. mSize = index+1;
  249. mHashes[index] = hash;
  250. index <<= 1;
  251. mArray[index] = key;
  252. mArray[index+1] = value;
  253. }

ArraySet用来替换HashSet,实现与ArrayMap类似,只不过ArraySet实现Collection<E>接口,ArrayMap实现Map接口具体不在详细说明

HashMap结构是以array存储链表的头结点,找到头结点后在进行遍历查找,如下图:

从图示中可以看出当HashMap扩容容量过多,元素较少时会产生内存使用不平衡,即浪费不少内存,而ArrayMap则不会有过多的内存浪费问题,

虽然效率比Hashmap低一些但是内存使用率有很大提高,采用时间换空间方式解决移动设备内存问题。

summary:

1,android中采用用时间换空间的方式,平衡移动设备内存问题而使用SparseArray,ArrayMap替换HashMap

2,SparseArray使用int[],为Integer类型key存储,Object[]为value即双数组一一对应的方式实现存储,替HashMap<Integer,Object> ArrayMap使用int[] hash 存储hashcode,Object[] mArrays偶数索引存储key,奇数索引存储value巧妙方式存储keyPair

3,当key为int类型value为reference object可以使用SparseArray,value为基本类型时使用使用SparsexxxArray,当key为其它引用类型时使用ArrayMap<K, V>替换HashMap

Android 之Map容器替换 SparseArray,ArrayMap,ArraySet的更多相关文章

  1. Android内存优化(使用SparseArray和ArrayMap代替HashMap)

    在Android开发时,我们使用的大部分都是Java的api,比如HashMap这个api,使用率非常高,但是对于Android这种对内存非常敏感的移动平台,很多时候使用一些java的api并不能达到 ...

  2. Android内存优化(使用SparseArray和ArrayMap取代HashMap)

    在Android开发时,我们使用的大部分都是Java的api,比方HashMap这个api,使用率非常高,可是对于Android这样的对内存非常敏感的移动平台,非常多时候使用一些java的api并不能 ...

  3. 【C++】map容器的用法

    检测map容器是否为空: 1 #include <iostream> 2 #include<map> 3 #include<string> 4 using name ...

  4. map 容器的使用

    C++中map容器提供一个键值对容器,map与multimap差别仅仅在于multiple允许一个键对应多个值. 一.map的说明    1   头文件   #include   <map> ...

  5. 一种map容器遍历的方法

    遍历算法是一种很常见而且非常重要的算法,我们用map容器的时候可能用的比较多的是查找,我今天才第一次要用到遍历.下面举个例子就知道了. map<string,string> mp; str ...

  6. CSU 1113 Updating a Dictionary(map容器应用)

    题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1113 解题报告:输入两个字符串,第一个是原来的字典,第二个是新字典,字典中的元素的格式为 ...

  7. map容器

    map容器一般用于对字符串进行编号,主要用于建图方面,例如把城市名按数字进行编号 #include"stdio.h" #include"string.h" #i ...

  8. Android google map 两点之间的距离

    在Android google map中,有时候会碰到计算两地的距离,下面的辅助类就可以帮助你计算距离: public class DistanceHelper { /** Names for the ...

  9. Tangled in Cables(Kruskal+map容器处理字符串)

    /** 题意:     给你两个城市之间的道路(无向图),求出需要的     电缆.如果大于所提供的,就输出Not enough ...     否则输出所需要的电缆长度.       输入:N (给 ...

  10. (6)Xamarin.android google map v2

    原文 Xamarin.android google map v2 Google Map v1已经在2013年的3月开始停止支持了,目前若要在你的Android手机上使用到Google Map,就必须要 ...

随机推荐

  1. 什么是django中间件?(七个中间件-自定义中间件)

    目录 一:django中间件 1.什么是django中间件 2.django请求生命周期流程图 二:django自带七个中间件 1.研究django中间件代码规律 2.django支持程序员自定义中间 ...

  2. python循环结构之for循环

    在python中,for循环是应用非常广的循环语句,遍历字典.遍历列表等等... # for语句结构 for 遍历 in 序列: 执行语句 遍历字典 lipsticks = {"Chanel ...

  3. C# 正则表达式常用的符号和模式解析

    〇.正则表达式的基本语法符号 若只简单匹配固定字符串,则无需任何修饰符,例如:需要匹配字符串 77,则可直接写:new Regex("77"). 下边例举一下常用的符号:(知道下面 ...

  4. 就聊聊不少小IT公司的技术总监

    本文想告诉大家如下两个观点. 1 很多IT小公司的技术总监,论能力其实也就是相当于大公司的高级程序员. 2 程序员在职业发展过程中,绝对应该优先考虑进大厂或好公司.如果仅仅停留在小公司,由于小公司可能 ...

  5. [常用工具] Python视频解码库DeFFcode使用指北

    DeFFcode是一种跨平台的高性能视频帧解码器,通过内部封装ffmpeg,提供GPU解码支持,几行python代码就能够快速解码视频帧,并具有强大的错误处理能力.DeFFcode的APIs支持多种媒 ...

  6. 迁移学习(MixMatch)《MixMatch: A Holistic Approach to Semi-Supervised Learning》

    论文信息 论文标题:MixMatch: A Holistic Approach to Semi-Supervised Learning论文作者:David Berthelot, Nicholas Ca ...

  7. [Leetcode] 寻找数组的中心索引

    题目 代码 class Solution { public: int pivotIndex(vector<int>& nums) { int right=0; for(auto i ...

  8. react 高效高质量搭建后台系统 系列 —— 登录

    其他章节请看: react 高效高质量搭建后台系统 系列 登录 本篇将完成登录模块.效果和 spug 相同: 需求如下: 登录页的绘制 支持普通登录和LDAP登录 登录成功后跳转到主页,没有登录的情况 ...

  9. 联邦学习(Federated Learning)

    联邦学习的思想概括为:一种无需交换数据(只交换训练中间参数或结果)的分布式机器学习技术,在保护数据隐私的同时实现数据共享,解决数据孤岛问题. 本文仅介绍基本概念,详细请查看文末参考资料. 基本概念 联 ...

  10. STL容器vector

    一.什么是Vector        向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container).跟任意其它类型容器一样,它能够存放各种类型的对象.可以简单的认为, ...