由于网上有朋友对于这个问题已经有了很详细的研究,所以我就不班门弄斧了:
转载于:http://android-performance.com/android/2014/02/10/android-sparsearray-vs-hashmap.html
http://liuzhichao.com/p/832.html
http://www.codes51.com/article/detail_163576.html
源码:
1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.util; 17 import com.android.internal.util.ArrayUtils; 18 import com.android.internal.util.GrowingArrayUtils; 19 import libcore.util.EmptyArray; 20 /** 21 * SparseArrays map integers to Objects. Unlike a normal array of Objects, 22 * there can be gaps in the indices. It is intended to be more memory efficient 23 * than using a HashMap to map Integers to Objects, both because it avoids 24 * auto-boxing keys and its data structure doesn't rely on an extra entry object 25 * for each mapping. 26 * 27 * <p>Note that this container keeps its mappings in an array data structure, 28 * using a binary search to find keys. The implementation is not intended to be appropriate for 29 * data structures 30 * that may contain large numbers of items. It is generally slower than a traditional 31 * HashMap, since lookups require a binary search and adds and removes require inserting 32 * and deleting entries in the array. For containers holding up to hundreds of items, 33 * the performance difference is not significant, less than 50%.</p> 34 * 35 * <p>To help with performance, the container includes an optimization when removing 36 * keys: instead of compacting its array immediately, it leaves the removed entry marked 37 * as deleted. The entry can then be re-used for the same key, or compacted later in 38 * a single garbage collection step of all removed entries. This garbage collection will 39 * need to be performed at any time the array needs to be grown or the the map size or 40 * entry values are retrieved.</p> 41 * 42 * <p>It is possible to iterate over the items in this container using 43 * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using 44 * <code>keyAt(int)</code> with ascending values of the index will return the 45 * keys in ascending order, or the values corresponding to the keys in ascending 46 * order in the case of <code>valueAt(int)</code>.</p> 47 */ 48 public class SparseArray<E> implements Cloneable { 49 private static final Object DELETED = new Object(); 50 private boolean mGarbage = false; 51 private int[] mKeys; 52 private Object[] mValues; 53 private int mSize; 54 /** 55 * Creates a new SparseArray containing no mappings. 56 */ 57 public SparseArray() { 58 this(10); 59 } 60 /** 61 * Creates a new SparseArray containing no mappings that will not 62 * require any additional memory allocation to store the specified 63 * number of mappings. If you supply an initial capacity of 0, the 64 * sparse array will be initialized with a light-weight representation 65 * not requiring any additional array allocations. 66 */ 67 public SparseArray(int initialCapacity) { 68 if (initialCapacity == 0) { 69 mKeys = EmptyArray.INT; 70 mValues = EmptyArray.OBJECT; 71 } else { 72 mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity); 73 mKeys = new int[mValues.length]; 74 } 75 mSize = 0; 76 } 77 @Override 78 @SuppressWarnings("unchecked") 79 public SparseArray<E> clone() { 80 SparseArray<E> clone = null; 81 try { 82 clone = (SparseArray<E>) super.clone(); 83 clone.mKeys = mKeys.clone(); 84 clone.mValues = mValues.clone(); 85 } catch (CloneNotSupportedException cnse) { 86 /* ignore */ 87 } 88 return clone; 89 } 90 /** 91 * Gets the Object mapped from the specified key, or <code>null</code> 92 * if no such mapping has been made. 93 */ 94 public E get(int key) { 95 return get(key, null); 96 } 97 /** 98 * Gets the Object mapped from the specified key, or the specified Object 99 * if no such mapping has been made. 100 */ 101 @SuppressWarnings("unchecked") 102 public E get(int key, E valueIfKeyNotFound) { 103 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 104 if (i < 0 || mValues[i] == DELETED) { 105 return valueIfKeyNotFound; 106 } else { 107 return (E) mValues[i]; 108 } 109 } 110 /** 111 * Removes the mapping from the specified key, if there was any. 112 */ 113 public void delete(int key) { 114 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 115 if (i >= 0) { 116 if (mValues[i] != DELETED) { 117 mValues[i] = DELETED; 118 mGarbage = true; 119 } 120 } 121 } 122 /** 123 * @hide 124 * Removes the mapping from the specified key, if there was any, returning the old value. 125 */ 126 public E removeReturnOld(int key) { 127 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 128 if (i >= 0) { 129 if (mValues[i] != DELETED) { 130 final E old = (E) mValues[i]; 131 mValues[i] = DELETED; 132 mGarbage = true; 133 return old; 134 } 135 } 136 return null; 137 } 138 /** 139 * Alias for {@link #delete(int)}. 140 */ 141 public void remove(int key) { 142 delete(key); 143 } 144 /** 145 * Removes the mapping at the specified index. 146 */ 147 public void removeAt(int index) { 148 if (mValues[index] != DELETED) { 149 mValues[index] = DELETED; 150 mGarbage = true; 151 } 152 } 153 /** 154 * Remove a range of mappings as a batch. 155 * 156 * @param index Index to begin at 157 * @param size Number of mappings to remove 158 */ 159 public void removeAtRange(int index, int size) { 160 final int end = Math.min(mSize, index + size); 161 for (int i = index; i < end; i++) { 162 removeAt(i); 163 } 164 } 165 private void gc() { 166 // Log.e("SparseArray", "gc start with " + mSize); 167 int n = mSize; 168 int o = 0; 169 int[] keys = mKeys; 170 Object[] values = mValues; 171 for (int i = 0; i < n; i++) { 172 Object val = values[i]; 173 if (val != DELETED) { 174 if (i != o) { 175 keys[o] = keys[i]; 176 values[o] = val; 177 values[i] = null; 178 } 179 o++; 180 } 181 } 182 mGarbage = false; 183 mSize = o; 184 // Log.e("SparseArray", "gc end with " + mSize); 185 } 186 /** 187 * Adds a mapping from the specified key to the specified value, 188 * replacing the previous mapping from the specified key if there 189 * was one. 190 */ 191 public void put(int key, E value) { 192 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 193 if (i >= 0) { 194 mValues[i] = value; 195 } else { 196 i = ~i; 197 if (i < mSize && mValues[i] == DELETED) { 198 mKeys[i] = key; 199 mValues[i] = value; 200 return; 201 } 202 if (mGarbage && mSize >= mKeys.length) { 203 gc(); 204 // Search again because indices may have changed. 205 i = ~ContainerHelpers.binarySearch(mKeys, mSize, key); 206 } 207 mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); 208 mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); 209 mSize++; 210 } 211 } 212 /** 213 * Returns the number of key-value mappings that this SparseArray 214 * currently stores. 215 */ 216 public int size() { 217 if (mGarbage) { 218 gc(); 219 } 220 return mSize; 221 } 222 /** 223 * Given an index in the range <code>0...size()-1</code>, returns 224 * the key from the <code>index</code>th key-value mapping that this 225 * SparseArray stores. 226 * 227 * <p>The keys corresponding to indices in ascending order are guaranteed to 228 * be in ascending order, e.g., <code>keyAt(0)</code> will return the 229 * smallest key and <code>keyAt(size()-1)</code> will return the largest 230 * key.</p> 231 */ 232 public int keyAt(int index) { 233 if (mGarbage) { 234 gc(); 235 } 236 return mKeys[index]; 237 } 238 /** 239 * Given an index in the range <code>0...size()-1</code>, returns 240 * the value from the <code>index</code>th key-value mapping that this 241 * SparseArray stores. 242 * 243 * <p>The values corresponding to indices in ascending order are guaranteed 244 * to be associated with keys in ascending order, e.g., 245 * <code>valueAt(0)</code> will return the value associated with the 246 * smallest key and <code>valueAt(size()-1)</code> will return the value 247 * associated with the largest key.</p> 248 */ 249 @SuppressWarnings("unchecked") 250 public E valueAt(int index) { 251 if (mGarbage) { 252 gc(); 253 } 254 return (E) mValues[index]; 255 } 256 /** 257 * Given an index in the range <code>0...size()-1</code>, sets a new 258 * value for the <code>index</code>th key-value mapping that this 259 * SparseArray stores. 260 */ 261 public void setValueAt(int index, E value) { 262 if (mGarbage) { 263 gc(); 264 } 265 mValues[index] = value; 266 } 267 /** 268 * Returns the index for which {@link #keyAt} would return the 269 * specified key, or a negative number if the specified 270 * key is not mapped. 271 */ 272 public int indexOfKey(int key) { 273 if (mGarbage) { 274 gc(); 275 } 276 return ContainerHelpers.binarySearch(mKeys, mSize, key); 277 } 278 /** 279 * Returns an index for which {@link #valueAt} would return the 280 * specified key, or a negative number if no keys map to the 281 * specified value. 282 * <p>Beware that this is a linear search, unlike lookups by key, 283 * and that multiple keys can map to the same value and this will 284 * find only one of them. 285 * <p>Note also that unlike most collections' {@code indexOf} methods, 286 * this method compares values using {@code ==} rather than {@code equals}. 287 */ 288 public int indexOfValue(E value) { 289 if (mGarbage) { 290 gc(); 291 } 292 for (int i = 0; i < mSize; i++) 293 if (mValues[i] == value) 294 return i; 295 return -1; 296 } 297 /** 298 * Removes all key-value mappings from this SparseArray. 299 */ 300 public void clear() { 301 int n = mSize; 302 Object[] values = mValues; 303 for (int i = 0; i < n; i++) { 304 values[i] = null; 305 } 306 mSize = 0; 307 mGarbage = false; 308 } 309 /** 310 * Puts a key/value pair into the array, optimizing for the case where 311 * the key is greater than all existing keys in the array. 312 */ 313 public void append(int key, E value) { 314 if (mSize != 0 && key <= mKeys[mSize - 1]) { 315 put(key, value); 316 return; 317 } 318 if (mGarbage && mSize >= mKeys.length) { 319 gc(); 320 } 321 mKeys = GrowingArrayUtils.append(mKeys, mSize, key); 322 mValues = GrowingArrayUtils.append(mValues, mSize, value); 323 mSize++; 324 } 325 /** 326 * {@inheritDoc} 327 * 328 * <p>This implementation composes a string by iterating over its mappings. If 329 * this map contains itself as a value, the string "(this Map)" 330 * will appear in its place. 331 */ 332 @Override 333 public String toString() { 334 if (size() <= 0) { 335 return "{}"; 336 } 337 StringBuilder buffer = new StringBuilder(mSize * 28); 338 buffer.append('{'); 339 for (int i=0; i<mSize; i++) { 340 if (i > 0) { 341 buffer.append(", "); 342 } 343 int key = keyAt(i); 344 buffer.append(key); 345 buffer.append('='); 346 Object value = valueAt(i); 347 if (value != this) { 348 buffer.append(value); 349 } else { 350 buffer.append("(this Map)"); 351 } 352 } 353 buffer.append('}'); 354 return buffer.toString(); 355 } 356 }