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