SparseArray详解
来源:互联网 发布:淘宝软件有限公司 股权 编辑:程序博客网 时间:2024/05/18 01:49
Android开发中,经常会遇到选择多个标签或内容进行统一提交处理的情况;通常做法就是用HashMap来保存每个位置的选中状态;了解到Android专门为这种状况提供了SparseArray这个类。从字面理解就是稀疏数组,老规矩,进行一下深入的了解。
HashMap数据结构:
既然Android专门提供了这个类,相比于HashMap应该有很大的优势才对,先回顾一下HashMap的数据结构:
简单说就是一个数组+链表的结构,根据元素的哈希值分配到不同的Bucket里;
SparseArray数据结构:
那SparseArray是什么样的结构呢?看源码:
private int[] mKeys;private Object[] mValues;private int mSize;就是两个数组,看命名就知道 一个放key,一个放value
方法分析:
我们接着往下看它的相关方法实现:
/** * Appends an element to the end of the array, growing the array if there is no more room. * @param array The array to which to append the element. This must NOT be null. * @param currentSize The number of elements in the array. Must be less than or equal to * array.length. * @param element The element to append. * @return the array to which the element was appended. This may be different than the given * array. */ public static <T> T[] append(T[] array, int currentSize, T element) { assert currentSize <= array.length; if (currentSize + 1 > array.length) { @SuppressWarnings("unchecked") T[] newArray = ArrayUtils.newUnpaddedArray( (Class<T>) array.getClass().getComponentType(), growSize(currentSize)); System.arraycopy(array, 0, newArray, 0, currentSize); array = newArray; } array[currentSize] = element; return array; }
/* 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; } } }
/** * 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 < 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); } mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } }
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); }
两个添加方法 : append 和 put 一个删除方法 :delete 一个私有方法 : gc
先来分析一下append方法 :
append 方法直接添加一个entry到SparseArray中:代码逻辑很简单,我们来看一下内部使用了两个其他的方法:
T[] newArray = ArrayUtils.newUnpaddedArray( (Class<T>) array.getClass().getComponentType(), growSize(currentSize));System.arraycopy(array, 0, newArray, 0, currentSize);看代码意思,这里应该是一个数组扩容的操作,我们先看一下
ArrayUtils.newUnpaddedArray<span style="font-family: Arial, Helvetica, sans-serif;">(</span><span style="font-family: Arial, Helvetica, sans-serif;">(Class<T>) array.getClass().getComponentType(), growSize(currentSize));</span>看看ArrayUtils是怎么实现这个方法的 :
源码地址: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/com/android/internal/util/ArrayUtils.java
@SuppressWarnings("unchecked") public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) { return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen); }调用了VMRuntime,我们接着看这个类:http://androidxref.com/5.0.0_r2/xref/libcore/libart/src/main/java/dalvik/system/VMRuntime.java#260
/** * Returns an array of at least minLength, but potentially larger. The increased size comes from * avoiding any padding after the array. The amount of padding varies depending on the * componentType and the memory allocator implementation.*/public native Object newUnpaddedArray(Class<?> componentType, int minLength);native方法 ,看注释,返回一个新的数组,可能比设置的minLength大;(英文不好,没看太明白。。。。)
好吧,先不深究这个,毕竟我们不是来追它底层怎么实现的;
看下另外一个方法:
System.arraycopy(array, 0, newArray, 0, currentSize);几个参数什么意思呢?
自己查吧 。。。就是一个复制数组的方法
下一个方法,put方法:
这里也有很关键的两个操作:
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);我们来看下这两个方法是怎么实现的:
ContainerHelpers:https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/util/ContainerHelpers.java
// 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) { final int mid = (lo + hi) >>> 1; final 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 }
GrowingArrayUtils:https://android.googlesource.com/platform/frameworks/base/+/master/core/java/com/android/internal/util/GrowingArrayUtils.java
/** * Inserts an element into the array at the specified index, growing the array if there is no * more room. * * @param array The array to which to append the element. Must NOT be null. * @param currentSize The number of elements in the array. Must be less than or equal to * array.length. * @param element The element to insert. * @return the array to which the element was appended. This may be different than the given * array. */ public static <T> T[] insert(T[] array, int currentSize, int index, T element) { assert currentSize <= array.length; if (currentSize + 1 <= array.length) { System.arraycopy(array, index, array, index + 1, currentSize - index); array[index] = element; return array; } @SuppressWarnings("unchecked") T[] newArray = ArrayUtils.newUnpaddedArray((Class<T>)array.getClass().getComponentType(), growSize(currentSize)); System.arraycopy(array, 0, newArray, 0, index); newArray[index] = element; System.arraycopy(array, index, newArray, index + 1, array.length - index); return newArray; }
/** * Appends an element to the end of the array, growing the array if there is no more room. * @param array The array to which to append the element. This must NOT be null. * @param currentSize The number of elements in the array. Must be less than or equal to * array.length. * @param element The element to append. * @return the array to which the element was appended. This may be different than the given * array. */ public static <T> T[] append(T[] array, int currentSize, T element) { assert currentSize <= array.length; if (currentSize + 1 > array.length) { @SuppressWarnings("unchecked") T[] newArray = ArrayUtils.newUnpaddedArray( (Class<T>) array.getClass().getComponentType(), growSize(currentSize)); System.arraycopy(array, 0, newArray, 0, currentSize); array = newArray; } array[currentSize] = element; return array; }
好吧 ,看到有个append方法竟然 ,又回头翻了一下SparseArray的源码,果然也有调用
/** * Puts a key/value pair into the array, optimizing for the case where * the key is greater than all existing keys in the array. */ public void append(int key, E value) { if (mSize != 0 && key <= mKeys[mSize - 1]) { put(key, value); return; } if (mGarbage && mSize >= mKeys.length) { gc(); } mKeys = GrowingArrayUtils.append(mKeys, mSize, key); mValues = GrowingArrayUtils.append(mValues, mSize, value); mSize++; }源码都有了,想必大家都能看明白了。。。不错,二分法
关键就是这个
binarySearch二分查找结束后返回的位置也很重要 ,多看几次就明白了,哈哈
最后说一下delete方法和gc方法 :
delete方法中并没有直接把元素移除掉,而是将值改为DELETE这个固定值 ,并且将flag置为true;
这样就保证了不用每次remove的时候就调用一次gc进行压缩(怎么压缩的看gc实现)
结尾了哈 :
通过查看实现方案发现,其实SparseArray也并没有多神奇,数组结构查找快,但是增删的时候也是慢很多;相比于hashmap来说各有千秋吧。不过针对于我最开始描述的需求使用,简直再适合不过呀 。。数据量大且操作多的时候慎用哈 。。
0 0
- SparseArray详解
- SparseArray详解
- SparseArray详解
- Android -- SparseArray<E>详解
- SparseArray<E>详解
- Android SparseArray源码详解
- SparseArray<E>详解
- Android 之SparseArray<E>详解
- Android之SparseArray<E>详解
- Android 之SparseArray<E>详解
- Android 之SparseArray<E>详解
- SparseArray详解,我说SparseArray,你说要!
- SparseArray
- SparseArray
- SparseArray
- SparseArray
- SparseArray
- SparseArray
- 安装和运行
- 内嵌Activex视频播放的插件问题记录-1
- URL Parsing
- 护士工作站执行签名颜色不同的意思------运维日志18
- Python中的self
- SparseArray详解
- dede仿三一重工官网模板
- 2-6HDFS读取数据的过程+下一篇类加载器(未完)
- java 内部类和匿名内部类
- iOS开发之网络请求过程URL特殊字符的转换处理
- 云渲染
- Codeforces #341 E. Wet Shark and Blocks dp 矩阵优化
- 游戏中的三角学——Sprite Kit 和 Swift 教程(2)
- Handler引起的内存泄露