当前位置:   article > 正文

Android性能优化之使用SparseArray代替HashMap_sparsearrays替换hashmap

sparsearrays替换hashmap

 

最近在重构one的项目,其中用HashMap来缓存ActivityGroup加载过的View,Eclipse给出了一个警告,之前考虑项目进度没怎么在意,这次仔细看了下提示,如下:

Use new SparseArray<View> (...) instead for better performance

意思就是说用SparseArray来替代,以获取更好的性能。对SparseArray根本不熟悉,甚至都没听过,第一感觉应该是Android提供的类,于是F3进入SparseArray的源码,果不其然,确实是Android提供的一个工具类,部分源码如下:

  1. * Copyright (C) 2006 The Android Open Source Project
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. package android.util;
  16. import com.android.internal.util.ArrayUtils;
  17. /**
  18. * SparseArrays map integers to Objects. Unlike a normal array of Objects,
  19. * there can be gaps in the indices. It is intended to be more efficient
  20. * than using a HashMap to map Integers to Objects.
  21. */
  22. public class SparseArray<E> implements Cloneable {
  23. private static final Object DELETED = new Object();
  24. private boolean mGarbage = false;
  25. private int[] mKeys;
  26. private Object[] mValues;
  27. private int mSize;
  28. /**
  29. * Creates a new SparseArray containing no mappings.
  30. */
  31. public SparseArray() {
  32. this(10);
  33. }
  34. /**
  35. * Creates a new SparseArray containing no mappings that will not
  36. * require any additional memory allocation to store the specified
  37. * number of mappings.
  38. */
  39. public SparseArray(int initialCapacity) {
  40. initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
  41. mKeys = new int[initialCapacity];
  42. mValues = new Object[initialCapacity];
  43. mSize = 0;
  44. }
  45. @Override
  46. @SuppressWarnings("unchecked")
  47. public SparseArray<E> clone() {
  48. SparseArray<E> clone = null;
  49. try {
  50. clone = (SparseArray<E>) super.clone();
  51. clone.mKeys = mKeys.clone();
  52. clone.mValues = mValues.clone();
  53. } catch (CloneNotSupportedException cnse) {
  54. /* ignore */
  55. }
  56. return clone;
  57. }
  58. /**
  59. * Gets the Object mapped from the specified key, or <code>null</code>
  60. * if no such mapping has been made.
  61. */
  62. public E get(int key) {
  63. return get(key, null);
  64. }
  65. /**
  66. * Gets the Object mapped from the specified key, or the specified Object
  67. * if no such mapping has been made.
  68. */
  69. @SuppressWarnings("unchecked")
  70. public E get(int key, E valueIfKeyNotFound) {
  71. int i = binarySearch(mKeys, 0, mSize, key);
  72. if (i < 0 || mValues[i] == DELETED) {
  73. return valueIfKeyNotFound;
  74. } else {
  75. return (E) mValues[i];
  76. }
  77. }
  78. /**
  79. * Removes the mapping from the specified key, if there was any.
  80. */
  81. public void delete(int key) {
  82. int i = binarySearch(mKeys, 0, mSize, key);
  83. if (i >= 0) {
  84. if (mValues[i] != DELETED) {
  85. mValues[i] = DELETED;
  86. mGarbage = true;
  87. }
  88. }
  89. }
  90. /**
  91. * Alias for {@link #delete(int)}.
  92. */
  93. public void remove(int key) {
  94. delete(key);
  95. }
  96. /**
  97. * Removes the mapping at the specified index.
  98. */
  99. public void removeAt(int index) {
  100. if (mValues[index] != DELETED) {
  101. mValues[index] = DELETED;
  102. mGarbage = true;
  103. }
  104. }
  105. ...
mSize++;单纯从字面上来理解,SparseArray指的是稀疏数组(Sparse array),所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容。

继续阅读SparseArray的源码,从构造方法我们可以看出,它和一般的List一样,可以预先设置容器大小,默认的大小是10:

  1. public SparseArray() {
  2. this(10);
  3. }
  4. public SparseArray(int initialCapacity) {
  5. initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
  6. mKeys = new int[initialCapacity];
  7. mValues = new Object[initialCapacity];
  8. mSize = 0;
  9. }

再来看看它对数据的“增删改查”。

  1. public void put(int key, E value) {}
  2. public void append(int key, E value){}

修改数据起初以为只有setValueAt(int index, E value)可以修改数据,但后来发现put(int key, E value)也可以修改数据,我们查看put(int key, E value)的源码可知,在put数据之前,会先查找要put的数据是否已经存在,如果存在就是修改,不存在就添加。

  1. public void put(int key, E value) {
  2. int i = binarySearch(mKeys, 0, mSize, key);
  3. if (i >= 0) {
  4. mValues[i] = value;
  5. } else {
  6. i = ~i;
  7. if (i < mSize && mValues[i] == DELETED) {
  8. mKeys[i] = key;
  9. mValues[i] = value;
  10. return;
  11. }
  12. if (mGarbage && mSize >= mKeys.length) {
  13. gc();
  14. // Search again because indices may have changed.
  15. i = ~binarySearch(mKeys, 0, mSize, key);
  16. }
  17. if (mSize >= mKeys.length) {
  18. int n = ArrayUtils.idealIntArraySize(mSize + 1);
  19. int[] nkeys = new int[n];
  20. Object[] nvalues = new Object[n];
  21. // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
  22. System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
  23. System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
  24. mKeys = nkeys;
  25. mValues = nvalues;
  26. }
  27. if (mSize - i != 0) {
  28. // Log.e("SparseArray", "move " + (mSize - i));
  29. System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
  30. System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
  31. }
  32. mKeys[i] = key;
  33. mValues[i] = value;
  34. }
  35. }

所以,修改数据实际也有两种方法:

  1. public void put(int key, E value)
  2. public void setValueAt(int index, E value)

最后再来看看如何查找数据。有两个方法可以查询取值:

  1. public E get(int key)
  2. public E get(int key, E valueIfKeyNotFound)

其中get(int key)也只是调用了 get(int key,E valueIfKeyNotFound),最后一个从传参的变量名就能看出,传入的是找不到的时候返回的值.get(int key)当找不到的时候,默认返回null。

查看第几个位置的键:

public int keyAt(int index)

有一点需要注意的是,查看键所在位置,由于是采用二分法查找键的位置,所以找不到时返回小于0的数值,而不是返回-1。返回的负值是表示它在找不到时所在的位置。

查看第几个位置的值:

public E valueAt(int index)

查看值所在位置,没有的话返回-1:

public int indexOfValue(E value)

最后,发现其核心就是折半查找函数(binarySearch),算法设计的很不错。

  1. private static int binarySearch(int[] a, int start, int len, int key) {
  2. int high = start + len, low = start - 1, guess;
  3. while (high - low &gt; 1) {
  4. guess = (high + low) / 2;
  5. if (a[guess] &lt; key)
  6. low = guess;
  7. else
  8. high = guess;
  9. }
  10. if (high == start + len)
  11. return ~(start + len);
  12. else if (a[high] == key)
  13. return high;
  14. else
  15. return ~high;
  16. }
  17. 相应的也有SparseBooleanArray,用来取代HashMap<Integer, Boolean>,SparseIntArray用来取代HashMap<Integer, Integer>,大家有兴趣的可以研究。

总结

  1. SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的类,目的是提高效率,其核心是折半查找函数(binarySearch)。在Android中,当我们需要定义
  2. HashMap<Integer, E> hashMap = new HashMap<Integer, E>();
  3. 时,我们可以使用如下的方式来取得更好的性能.
  4. SparseArray<E> sparseArray = new SparseArray<E>();

最后,看看重构后的ActivityGroupManager:

  1. public class ActivityGroupManager {
  2. static final String TAG = ActivityGroupManager.class.getName();
  3. private SparseArray<View> array;
  4. private ViewGroup container;
  5. public ActivityGroupManager() {
  6. array = new SparseArray<View>();
  7. }
  8. public void setContainer(ViewGroup container) {
  9. this.container = container;
  10. }
  11. public void showContainer(int num, View view) {
  12. if (array.get(num) == null) {
  13. array.put(num, view);
  14. container.addView(view);
  15. }
  16. for (int i = 0; i < array.size(); i++) {
  17. View v = array.valueAt(i);
  18. v.setVisibility(View.GONE);
  19. }
  20. view.setVisibility(View.VISIBLE);
  21. }
  22. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/738286
推荐阅读
相关标签
  

闽ICP备14008679号