• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }