当前位置:   article > 正文

SparseArray到底哪点比HashMap好_比较sparsearray和hashmap的特性

比较sparsearray和hashmap的特性

SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的class,目的是提高效率,其核心是折半查找函数(binarySearch)。

HashMap底层是一个Hash表,是数组和链表的集合实现,有需要的可以去看看我关于Hashmap的分析。hashmap源码分析

所以Android开发中官方推荐:当使用HashMap(K, V),如果K为整数类型时,使用SparseArray的效率更高。

那我们看源码来分析下,

构造函数:

  1. /**
  2. * 存储索引集合.
  3. */
  4. private int[] mKeys;
  5. /**
  6. * 存储对象集合.
  7. */
  8. private Object[] mValues;
  9. /**
  10. * 存储的键值对总数.
  11. */
  12. private int mSize;
  13. /**
  14. * 采用默认的构造函数,则初始容量为10.
  15. */
  16. public SparseArray() {
  17. this(10);
  18. }
  19. /**
  20. * 使用指定的初始容量构造SparseArray.
  21. *
  22. * @param initialCapacity 初始容量
  23. */
  24. public SparseArray(int initialCapacity) {
  25. if (initialCapacity == 0) {
  26. // Effective Java中第43条:返回零长度的数组或者集合,而不是:null
  27. mKeys = ContainerHelpers.EMPTY_INTS;
  28. mValues = ContainerHelpers.EMPTY_OBJECTS;
  29. } else {
  30. // 构造initialCapacity大小的int数组和object数组
  31. mKeys = new int[initialCapacity];
  32. mValues = new Object[initialCapacity];
  33. }
  34. // 设置SparseArray存储的<key,value>键值对个数为0.
  35. mSize = 0;
  36. }
和HashMap的数据结构不同,HashMap是使用 数组+链表 的数据结构存储键值对,而SparseArray只是用了 两个数组 进行存储。

我们知道链表的时间复杂度是很高的,这估计也是造成hashmap时间复杂度高的一个原因。

ContainerHelpers

ContainerHelpers类提供了二分查找算法,这也一定程度上提高了查找的效率

  1. <span style="font-size:12px;">class ContainerHelpers {
  2. // This is Arrays.binarySearch(), but doesn't do any argument validation.
  3. static int binarySearch(int[] array, int size, int value) {
  4. // 获取二分的起始和结束下标.
  5. int lo = 0;
  6. int hi = size - 1;
  7. while (lo <= hi) {
  8. // 获取中点的下标和值
  9. final int mid = (lo + hi) >>> 1;
  10. final int midVal = array[mid];
  11. if (midVal < value) {
  12. lo = mid + 1;
  13. } else if (midVal > value) {
  14. hi = mid - 1;
  15. } else {
  16. return mid; // value found
  17. }
  18. }
  19. return ~lo; // value not present
  20. }
  21. }</span>

put()函数

  1. /**
  2. * 在SparseArray中存储键值对.
  3. */
  4. public void put(int key, E value) {
  5. // 通过二分查找算法计算索引
  6. int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
  7. if (i >= 0) {
  8. // key已经存在对应的value,则直接替换value.
  9. mValues[i] = value;
  10. } else {
  11. i = ~i;
  12. if (i < mSize && mValues[i] == DELETED) {
  13. // 特殊的case,直接存储key-value即可
  14. mKeys[i] = key;
  15. mValues[i] = value;
  16. return;
  17. }
  18. if (mGarbage && mSize >= mKeys.length) {
  19. // 如果有元素被删除,并且目前容量不足,先进行一次gc
  20. gc();
  21. // Search again because indices may have changed.
  22. i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
  23. }
  24. // 扩容
  25. if (mSize >= mKeys.length) {
  26. // 获取扩容的数组大小
  27. int n = mSize + 1;
  28. int[] nkeys = new int[n];
  29. Object[] nvalues = new Object[n];
  30. // 数组拷贝最好使用System.arraycopy,而不是自己重撸一遍
  31. System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
  32. System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
  33. mKeys = nkeys;
  34. mValues = nvalues;
  35. }
  36. // i为插入位置,如果i<mSize,则i之后的元素需要依次向后移动一位.
  37. if (mSize - i != 0) {
  38. System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
  39. System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
  40. }
  41. // 设置值,存储数量+1
  42. mKeys[i] = key;
  43. mValues[i] = value;
  44. mSize++;
  45. }
  46. }

put函数的逻辑:

  1. 通过二分查找算法,计算key的索引值.
  2. 如果索引值大于0,说明有key对应的value存在,直接替换value即可.
  3. 如果索引值小于0,对索引值取反,获取key应该插入的坐标i.
  4. 判断是否需要扩容:1.需要扩容,则先扩容; 2.不需要扩容,则利用System.arraycopy移动相应的元素,进行(key,value)键值对插入.

get()函数

get函数就是利用二分查找获取key的下标,然后从object[] value数组中根据下标获取值. 
之所以SparseArray号称比HashMap有更好的性能:

  1. SparseArray更加节约内存,一个int[]数组存储所有的key,一个object[] 数组存储所有的value.
  2. HashMap遇到冲突时,时间复杂度为O(n).而SparseArray不会有冲突,采用二分搜索算法,时间复杂度为O(lgn).
  1. /**
  2. * 根据指定的key获取value.
  3. */
  4. public E get(int key) {
  5. return get(key, null);
  6. }
  7. /**
  8. * 利用二分查找算法根据key获取指定的value.
  9. */
  10. public E get(int key, E valueIfKeyNotFound) {
  11. int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
  12. if (i < 0 || mValues[i] == DELETED) {
  13. return valueIfKeyNotFound;
  14. } else {
  15. return (E) mValues[i];
  16. }
  17. }

delete()函数

  1. /**
  2. * 根据key删除指定的value.
  3. */
  4. public void delete(int key) {
  5. int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
  6. if (i >= 0) {
  7. if (mValues[i] != DELETED) {
  8. // 标记i的值为private static final Object DELETED = new Object();
  9. mValues[i] = DELETED;
  10. // 设置gc标记为true.
  11. mGarbage = true;
  12. }
  13. }
  14. }
  15. /**
  16. * Alias for {@link #delete(int)}.
  17. */
  18. public void remove(int key) {
  19. delete(key);
  20. }

gc()函数

  1. if (mGarbage && mSize >= mKeys.length) {
  2. // 如果有元素被删除,并且目前容量不足,先进行一次gc
  3. gc();
  4. // Search again because indices may have changed.
  5. i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
  6. }
通过上面的源码分析,我们不难得出:
正式因为SparseArray采用了数组这种形式,才使得我们的key在做hash运算的时候,通过二分查找时间复杂度降低了,从而提高了效率。
通过二分查找保证查询效率为O(lgn).而HashMap在未冲突的情况下是O(1),冲突的情况下是O(n).





声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号