在Andorid的源码和第三方库中,偶尔能看到该类,咱们先来看一下官方文档的说明以下:数组
SparseArray 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.数据结构
上面的意思是SparseArray 用来替代HashMap Int到Object的这种关系。 它设计的目的是为了比HashMap更加节省内存,这是由于:app
Note that this container keeps its mappings in an array data structure,using a binary search to find keys. The implementation is not intended to be appropriate for data structures that may contain large numbers of items. It is generally slower than a traditional HashMap, since lookups require a binary search and adds and removes require inserting and deleting entries in the array. For containers holding up to hundreds of items,the performance difference is not significant, less than 50%.less
上面说了,SparseArray经过二分查找键值,这种实现方式不太适合太多的item。一般状况他是比HashMap慢的,可是若是容器只有数百的item,这个性能损失不过重要,不超过50%。性能
实际在Android环境中,咱们的键值也不多有超过上千的,因此SparseArray咱们能在项目中用到的地方仍是很多,若是在Android Stuido出现一个黄色警告叫你替换的话,你就能够考虑替换了。由于对于Android 来讲,内存每每比较重要一点。测试
下面咱们分析SparseArray的实现方式,从字面意思上翻译过来就是稀疏数组,首先咱们打开源码看一下SparseArray的结构是怎么样的:ui
public class SparseArray<E> implements Cloneable { private static final Object DELETED = new Object(); private boolean mGarbage = false; private int[] mKeys; private Object[] mValues; private int mSize; ........我是省略的代码哟.......... }
看的出代码中有一个 int[] mKeys 数组, 和一个 Object[] mValues 数组,这两个就是存放键值和对象的地方,mSize是咱们存入了多少键值对。下面咱们将要用一个很是土的办法来看看这个结构是怎么样的,咱们写一段测试代码以下,断点调试。this
1 SparseArray<Object> sparseArray = new SparseArray<>(); 2 sparseArray.put(1, "11"); 3 sparseArray.put(8, "13"); 4 sparseArray.put(4, "12"); 5 sparseArray.put(0, "30");
咱们执行第二行代码之后:google
mKey中的结构: {1,0,0,0,0,0,0,0,0,0,0,0} 后面的0是初始化mKey的长度 mValues中的结构: {"11"}
mSize: 1spa
咱们执行第三行代码之后:
mKey中的结构: {1,8,0,0,0,0,0,0,0,0,0,0} 后面的0是初始化mKey的长度 mValues中的结构: {"11","13"}
mSize: 2
咱们执行第三行代码之后:
mKey中的结构: {1,4,8,0,0,0,0,0,0,0,0,0} 后面的0是初始化mKey的长度 mValues中的结构: {"11","12","13"}
mSize: 3
咱们执行第三行代码之后:
mKey中的结构: {0,1,4,8,0,0,0,0,0,0,0,0,0} 后面的0是初始化mKey的长度 mValues中的结构: {"30","11","12","13"}
mSize: 4
从以上的结构中咱们能够判断出,若是咱们想查找一个键值为4的key,那么首先第一步找到key对应在mKey数组中的index,那么对应的value就在对应mValues中的index位置。再仔细观察上面的mKey中的结构,你会发现mKey中的数字是递增的,那么这保证了咱们就能够经过二分查找去找到某一个值在mkey中的位置。ok这个结构分析完毕以后,咱们接下来看看,增,删,查,找功能。
/** * 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 { //若是没有找到,这个返回的值就是没有找到的mid+1 那么再取反 正好是当前mKey的存有值 //的下一个值,只能说好巧妙~~ 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); } //GrowingArrayUtils 这个源码看不到,google下源码,就是他会动态增长数组的size, //经过System.arraycopy 实现的,具体本身去google咯 mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } }
/** * 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; } } }
/** * 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]; } }
看了以上的操做,都是经过二分查找来操做的,因此和HashMap相比, 性能仍是有所损失的,最后看看GC的操做
private void gc() { // Log.e("SparseArray", "gc start with " + mSize); int n = mSize; int o = 0; int[] keys = mKeys; Object[] values = mValues; //循环查找 找到了置空 for (int i = 0; i < n; i++) { Object val = values[i]; if (val != DELETED) { if (i != o) { keys[o] = keys[i]; values[o] = val; values[i] = null; } o++; } } mGarbage = false; mSize = o; // Log.e("SparseArray", "gc end with " + mSize); }
总结一下, 就是SpareArray 比HashMap更节约内存,可是性能不如HashMap,在Android系统这内存似金的年代,咱们仍是应该想尽各类办法去节约内存的。 SpareArray还有一个兄弟 叫LongSparsArray 实现原理也是同样的。