1 /* 2 * Copyright (C) 2009 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 17 package android.util; 18 19 import com.android.internal.util.ArrayUtils; 20 21 /** 22 * SparseArrays map longs to Objects. Unlike a normal array of Objects, 23 * there can be gaps in the indices. It is intended to be more efficient 24 * than using a HashMap to map Longs to Objects. 25 * 26 * @hide 27 */ 28 public class LongSparseArray<E> { 29 private static final Object DELETED = new Object(); 30 private boolean mGarbage = false; 31 32 /** 33 * Creates a new SparseArray containing no mappings. 34 */ LongSparseArray()35 public LongSparseArray() { 36 this(10); 37 } 38 39 /** 40 * Creates a new SparseArray containing no mappings that will not 41 * require any additional memory allocation to store the specified 42 * number of mappings. 43 */ LongSparseArray(int initialCapacity)44 public LongSparseArray(int initialCapacity) { 45 initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); 46 47 mKeys = new long[initialCapacity]; 48 mValues = new Object[initialCapacity]; 49 mSize = 0; 50 } 51 52 /** 53 * @return A copy of all keys contained in the sparse array. 54 */ getKeys()55 public long[] getKeys() { 56 int length = mKeys.length; 57 long[] result = new long[length]; 58 System.arraycopy(mKeys, 0, result, 0, length); 59 return result; 60 } 61 62 /** 63 * Sets all supplied keys to the given unique value. 64 * @param keys Keys to set 65 * @param uniqueValue Value to set all supplied keys to 66 */ setValues(long[] keys, E uniqueValue)67 public void setValues(long[] keys, E uniqueValue) { 68 int length = keys.length; 69 for (int i = 0; i < length; i++) { 70 put(keys[i], uniqueValue); 71 } 72 } 73 74 /** 75 * Gets the Object mapped from the specified key, or <code>null</code> 76 * if no such mapping has been made. 77 */ get(long key)78 public E get(long key) { 79 return get(key, null); 80 } 81 82 /** 83 * Gets the Object mapped from the specified key, or the specified Object 84 * if no such mapping has been made. 85 */ get(long key, E valueIfKeyNotFound)86 public E get(long key, E valueIfKeyNotFound) { 87 int i = binarySearch(mKeys, 0, mSize, key); 88 89 if (i < 0 || mValues[i] == DELETED) { 90 return valueIfKeyNotFound; 91 } else { 92 return (E) mValues[i]; 93 } 94 } 95 96 /** 97 * Removes the mapping from the specified key, if there was any. 98 */ delete(long key)99 public void delete(long key) { 100 int i = binarySearch(mKeys, 0, mSize, key); 101 102 if (i >= 0) { 103 if (mValues[i] != DELETED) { 104 mValues[i] = DELETED; 105 mGarbage = true; 106 } 107 } 108 } 109 110 /** 111 * Alias for {@link #delete(long)}. 112 */ remove(long key)113 public void remove(long key) { 114 delete(key); 115 } 116 gc()117 private void gc() { 118 // Log.e("SparseArray", "gc start with " + mSize); 119 120 int n = mSize; 121 int o = 0; 122 long[] keys = mKeys; 123 Object[] values = mValues; 124 125 for (int i = 0; i < n; i++) { 126 Object val = values[i]; 127 128 if (val != DELETED) { 129 if (i != o) { 130 keys[o] = keys[i]; 131 values[o] = val; 132 } 133 134 o++; 135 } 136 } 137 138 mGarbage = false; 139 mSize = o; 140 141 // Log.e("SparseArray", "gc end with " + mSize); 142 } 143 144 /** 145 * Adds a mapping from the specified key to the specified value, 146 * replacing the previous mapping from the specified key if there 147 * was one. 148 */ put(long key, E value)149 public void put(long key, E value) { 150 int i = binarySearch(mKeys, 0, mSize, key); 151 152 if (i >= 0) { 153 mValues[i] = value; 154 } else { 155 i = ~i; 156 157 if (i < mSize && mValues[i] == DELETED) { 158 mKeys[i] = key; 159 mValues[i] = value; 160 return; 161 } 162 163 if (mGarbage && mSize >= mKeys.length) { 164 gc(); 165 166 // Search again because indices may have changed. 167 i = ~binarySearch(mKeys, 0, mSize, key); 168 } 169 170 if (mSize >= mKeys.length) { 171 int n = ArrayUtils.idealIntArraySize(mSize + 1); 172 173 long[] nkeys = new long[n]; 174 Object[] nvalues = new Object[n]; 175 176 // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); 177 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); 178 System.arraycopy(mValues, 0, nvalues, 0, mValues.length); 179 180 mKeys = nkeys; 181 mValues = nvalues; 182 } 183 184 if (mSize - i != 0) { 185 // Log.e("SparseArray", "move " + (mSize - i)); 186 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); 187 System.arraycopy(mValues, i, mValues, i + 1, mSize - i); 188 } 189 190 mKeys[i] = key; 191 mValues[i] = value; 192 mSize++; 193 } 194 } 195 196 /** 197 * Returns the number of key-value mappings that this SparseArray 198 * currently stores. 199 */ size()200 public int size() { 201 if (mGarbage) { 202 gc(); 203 } 204 205 return mSize; 206 } 207 208 /** 209 * Given an index in the range <code>0...size()-1</code>, returns 210 * the key from the <code>index</code>th key-value mapping that this 211 * SparseArray stores. 212 */ keyAt(int index)213 public long keyAt(int index) { 214 if (mGarbage) { 215 gc(); 216 } 217 218 return mKeys[index]; 219 } 220 221 /** 222 * Given an index in the range <code>0...size()-1</code>, returns 223 * the value from the <code>index</code>th key-value mapping that this 224 * SparseArray stores. 225 */ valueAt(int index)226 public E valueAt(int index) { 227 if (mGarbage) { 228 gc(); 229 } 230 231 return (E) mValues[index]; 232 } 233 234 /** 235 * Given an index in the range <code>0...size()-1</code>, sets a new 236 * value for the <code>index</code>th key-value mapping that this 237 * SparseArray stores. 238 */ setValueAt(int index, E value)239 public void setValueAt(int index, E value) { 240 if (mGarbage) { 241 gc(); 242 } 243 244 mValues[index] = value; 245 } 246 247 /** 248 * Returns the index for which {@link #keyAt} would return the 249 * specified key, or a negative number if the specified 250 * key is not mapped. 251 */ indexOfKey(long key)252 public int indexOfKey(long key) { 253 if (mGarbage) { 254 gc(); 255 } 256 257 return binarySearch(mKeys, 0, mSize, key); 258 } 259 260 /** 261 * Returns an index for which {@link #valueAt} would return the 262 * specified key, or a negative number if no keys map to the 263 * specified value. 264 * Beware that this is a linear search, unlike lookups by key, 265 * and that multiple keys can map to the same value and this will 266 * find only one of them. 267 */ indexOfValue(E value)268 public int indexOfValue(E value) { 269 if (mGarbage) { 270 gc(); 271 } 272 273 for (int i = 0; i < mSize; i++) 274 if (mValues[i] == value) 275 return i; 276 277 return -1; 278 } 279 280 /** 281 * Removes all key-value mappings from this SparseArray. 282 */ clear()283 public void clear() { 284 int n = mSize; 285 Object[] values = mValues; 286 287 for (int i = 0; i < n; i++) { 288 values[i] = null; 289 } 290 291 mSize = 0; 292 mGarbage = false; 293 } 294 295 /** 296 * Puts a key/value pair into the array, optimizing for the case where 297 * the key is greater than all existing keys in the array. 298 */ append(long key, E value)299 public void append(long key, E value) { 300 if (mSize != 0 && key <= mKeys[mSize - 1]) { 301 put(key, value); 302 return; 303 } 304 305 if (mGarbage && mSize >= mKeys.length) { 306 gc(); 307 } 308 309 int pos = mSize; 310 if (pos >= mKeys.length) { 311 int n = ArrayUtils.idealIntArraySize(pos + 1); 312 313 long[] nkeys = new long[n]; 314 Object[] nvalues = new Object[n]; 315 316 // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); 317 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); 318 System.arraycopy(mValues, 0, nvalues, 0, mValues.length); 319 320 mKeys = nkeys; 321 mValues = nvalues; 322 } 323 324 mKeys[pos] = key; 325 mValues[pos] = value; 326 mSize = pos + 1; 327 } 328 binarySearch(long[] a, int start, int len, long key)329 private static int binarySearch(long[] a, int start, int len, long key) { 330 int high = start + len, low = start - 1, guess; 331 332 while (high - low > 1) { 333 guess = (high + low) / 2; 334 335 if (a[guess] < key) 336 low = guess; 337 else 338 high = guess; 339 } 340 341 if (high == start + len) 342 return ~(start + len); 343 else if (a[high] == key) 344 return high; 345 else 346 return ~high; 347 } 348 checkIntegrity()349 private void checkIntegrity() { 350 for (int i = 1; i < mSize; i++) { 351 if (mKeys[i] <= mKeys[i - 1]) { 352 for (int j = 0; j < mSize; j++) { 353 Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); 354 } 355 356 throw new RuntimeException(); 357 } 358 } 359 } 360 361 private long[] mKeys; 362 private Object[] mValues; 363 private int mSize; 364 }