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