SparseArray源码中的G点
2016-08-08 10:59
155 查看
通过上一章 小探Android中的SparseArray和HashMap了解了SparseArray和HashMap的一点区别,以及SparseArray的使用场景。这一章主要来记录一下查看SparseArray源码时,个人感觉源码中值得借鉴的地方:二分查找
代码如下:
size : 查找数组的范围,一般情况下需要查找整个数组,因此传的是数组的长度
value : 查找的对象
判断算出的中间值是否与value值相等,如果成立说明找到,则返回下标
如果中间值小于value,则将中间值+1重新置为hi值,再计算出新的mid值,重新执行步骤2
如果大于value,则将中间值-1重新置为low值,再计算出新的mid值,重新执行步骤2
如果循环执行完之后还是没有找到,则将low取反并返回
以上有计算机基础的应该都能想到,具体就不详说了, 此处想指出的是谷歌工程师写代码的习惯:
第7行计算中间值时并不是使用XXX / 2,而是使用 >>>向右移一位 吊!
第18行返回lo取反值,为什么要这么做呢?
举个很简单的例子,如下数字:
如果按照上述二分查找的方法查找20,则最后返回的是 -5. -5这个数很有意思,设想一下如果我们想往arr中新加一个元素,并且以升序的方式添加,那下一个被添加的元素下标正好是 5 !! 对的,我们只需要判断一下二分查找返回的值是否小于0, 如果小于0,则说明在arr中并没有找到20,因此我们只需要将20放置在返回值取反的位置即可,这样arr始终都是以升序的方式排列的。如果我们设想的没有错,那SparseArray存储方式应该与我们想的一致,如下代码:
代码如下:
// 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 }
以上二分查找需要传入三个参数:
array : 需要查找的数组对象size : 查找数组的范围,一般情况下需要查找整个数组,因此传的是数组的长度
value : 查找的对象
具体思路就是:
先找到数组的0和最后一个下标的值,然后算出中间值判断算出的中间值是否与value值相等,如果成立说明找到,则返回下标
如果中间值小于value,则将中间值+1重新置为hi值,再计算出新的mid值,重新执行步骤2
如果大于value,则将中间值-1重新置为low值,再计算出新的mid值,重新执行步骤2
如果循环执行完之后还是没有找到,则将low取反并返回
以上有计算机基础的应该都能想到,具体就不详说了, 此处想指出的是谷歌工程师写代码的习惯:
第7行计算中间值时并不是使用XXX / 2,而是使用 >>>向右移一位 吊!
第18行返回lo取反值,为什么要这么做呢?
举个很简单的例子,如下数字:
int[] arr = new int[]{1, 4, 8, 10, 15};
如果按照上述二分查找的方法查找20,则最后返回的是 -5. -5这个数很有意思,设想一下如果我们想往arr中新加一个元素,并且以升序的方式添加,那下一个被添加的元素下标正好是 5 !! 对的,我们只需要判断一下二分查找返回的值是否小于0, 如果小于0,则说明在arr中并没有找到20,因此我们只需要将20放置在返回值取反的位置即可,这样arr始终都是以升序的方式排列的。如果我们设想的没有错,那SparseArray存储方式应该与我们想的一致,如下代码:
/** * 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++; } }
Bingo,果然如我们所料!!
注意:SparseArray时间与空间的取舍!
根据上一章的描述可以知道,SparseArray插入一个值时,首先找到要插入的下标,保证数组有序,这样就需要移动里面的元素,插入时key和value都要移动一次,时间复杂度是O(N),但是HashMap不需要做这个操作。SparseArray主要是减少了空的占用。时间和空间的关系往往就是牺牲时间换空间,或者牺牲空间换时间相关文章推荐
- 深入分析Android系统中SparseArray的源码
- 菜鸟看源码之SparseArray
- SparseArray源码阅读
- Android SparseArray 源码详解
- SparseArray和ArrayMap相关源码解析
- Android SparseArray源码分析
- android sparseArray源码解析
- Android源码-SparseArray
- Android探索之旅(第十二篇)HashMap,ArrayMap,SparseArray源码分析及性能对比
- 内存优化之SparseArray源码分析
- HashMap,ArrayMap,SparseArray源码分析及性能对比
- Android中SparseArray源码实现
- 谈谈源码中的SparseArray
- android sparseArray源码解析,比较hashmap
- android SparseArray 源码分析
- Android SparseArray 源码解析
- [转帖]Mootools源码分析-04 -- Array
- Android性能优化之谈谈SparseArray,SparseBooleanArray和SparseIntArray
- Android SparseArray与HashMap与ArrayMap的性能差别
- Android性能优化之谈谈SparseArray,SparseBooleanArray和SparseIntArray