blob: 630e5f37d8a026f93f5f092d94b21a7c4fb034b7 [file] [log] [blame]
Romain Guyfdbf6a72009-06-18 15:13:40 -07001/*
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
17package android.util;
18
19import com.android.internal.util.ArrayUtils;
20
21/**
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080022 * SparseArray mapping longs to Objects. Unlike a normal array of Objects,
Romain Guyfdbf6a72009-06-18 15:13:40 -070023 * there can be gaps in the indices. It is intended to be more efficient
24 * than using a HashMap to map Longs to Objects.
Romain Guyfdbf6a72009-06-18 15:13:40 -070025 */
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080026public class LongSparseArray<E> implements Cloneable {
Romain Guyfdbf6a72009-06-18 15:13:40 -070027 private static final Object DELETED = new Object();
28 private boolean mGarbage = false;
29
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080030 private long[] mKeys;
31 private Object[] mValues;
32 private int mSize;
33
Romain Guyfdbf6a72009-06-18 15:13:40 -070034 /**
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080035 * Creates a new LongSparseArray containing no mappings.
Romain Guyfdbf6a72009-06-18 15:13:40 -070036 */
37 public LongSparseArray() {
38 this(10);
39 }
40
41 /**
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080042 * Creates a new LongSparseArray containing no mappings that will not
Romain Guyfdbf6a72009-06-18 15:13:40 -070043 * require any additional memory allocation to store the specified
44 * number of mappings.
45 */
46 public LongSparseArray(int initialCapacity) {
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080047 initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
Romain Guyfdbf6a72009-06-18 15:13:40 -070048
49 mKeys = new long[initialCapacity];
50 mValues = new Object[initialCapacity];
51 mSize = 0;
52 }
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080053
54 @Override
55 @SuppressWarnings("unchecked")
56 public LongSparseArray<E> clone() {
57 LongSparseArray<E> clone = null;
58 try {
59 clone = (LongSparseArray<E>) super.clone();
60 clone.mKeys = mKeys.clone();
61 clone.mValues = mValues.clone();
62 } catch (CloneNotSupportedException cnse) {
63 /* ignore */
Adam Powell8f1bfe12010-03-05 15:13:56 -080064 }
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080065 return clone;
Adam Powell8f1bfe12010-03-05 15:13:56 -080066 }
Romain Guyfdbf6a72009-06-18 15:13:40 -070067
68 /**
69 * Gets the Object mapped from the specified key, or <code>null</code>
70 * if no such mapping has been made.
71 */
72 public E get(long key) {
73 return get(key, null);
74 }
75
76 /**
77 * Gets the Object mapped from the specified key, or the specified Object
78 * if no such mapping has been made.
79 */
Dianne Hackborn9c1d2982012-02-10 12:36:18 -080080 @SuppressWarnings("unchecked")
Romain Guyfdbf6a72009-06-18 15:13:40 -070081 public E get(long key, E valueIfKeyNotFound) {
82 int i = binarySearch(mKeys, 0, mSize, key);
83
84 if (i < 0 || mValues[i] == DELETED) {
85 return valueIfKeyNotFound;
86 } else {
87 return (E) mValues[i];
88 }
89 }
90
91 /**
92 * Removes the mapping from the specified key, if there was any.
93 */
94 public void delete(long key) {
95 int i = binarySearch(mKeys, 0, mSize, key);
96
97 if (i >= 0) {
98 if (mValues[i] != DELETED) {
99 mValues[i] = DELETED;
100 mGarbage = true;
101 }
102 }
103 }
104
105 /**
106 * Alias for {@link #delete(long)}.
107 */
108 public void remove(long key) {
109 delete(key);
110 }
111
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800112 /**
113 * Removes the mapping at the specified index.
114 */
115 public void removeAt(int index) {
116 if (mValues[index] != DELETED) {
117 mValues[index] = DELETED;
118 mGarbage = true;
119 }
120 }
121
Romain Guyfdbf6a72009-06-18 15:13:40 -0700122 private void gc() {
123 // Log.e("SparseArray", "gc start with " + mSize);
124
125 int n = mSize;
126 int o = 0;
127 long[] keys = mKeys;
128 Object[] values = mValues;
129
130 for (int i = 0; i < n; i++) {
131 Object val = values[i];
132
133 if (val != DELETED) {
134 if (i != o) {
135 keys[o] = keys[i];
136 values[o] = val;
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800137 values[i] = null;
Romain Guyfdbf6a72009-06-18 15:13:40 -0700138 }
139
140 o++;
141 }
142 }
143
144 mGarbage = false;
145 mSize = o;
146
147 // Log.e("SparseArray", "gc end with " + mSize);
148 }
149
150 /**
151 * Adds a mapping from the specified key to the specified value,
152 * replacing the previous mapping from the specified key if there
153 * was one.
154 */
155 public void put(long key, E value) {
156 int i = binarySearch(mKeys, 0, mSize, key);
157
158 if (i >= 0) {
159 mValues[i] = value;
160 } else {
161 i = ~i;
162
163 if (i < mSize && mValues[i] == DELETED) {
164 mKeys[i] = key;
165 mValues[i] = value;
166 return;
167 }
168
169 if (mGarbage && mSize >= mKeys.length) {
170 gc();
171
172 // Search again because indices may have changed.
173 i = ~binarySearch(mKeys, 0, mSize, key);
174 }
175
176 if (mSize >= mKeys.length) {
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800177 int n = ArrayUtils.idealLongArraySize(mSize + 1);
Romain Guyfdbf6a72009-06-18 15:13:40 -0700178
179 long[] nkeys = new long[n];
180 Object[] nvalues = new Object[n];
181
182 // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
183 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
184 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
185
186 mKeys = nkeys;
187 mValues = nvalues;
188 }
189
190 if (mSize - i != 0) {
191 // Log.e("SparseArray", "move " + (mSize - i));
192 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
193 System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
194 }
195
196 mKeys[i] = key;
197 mValues[i] = value;
198 mSize++;
199 }
200 }
201
202 /**
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800203 * Returns the number of key-value mappings that this LongSparseArray
Romain Guyfdbf6a72009-06-18 15:13:40 -0700204 * currently stores.
205 */
206 public int size() {
207 if (mGarbage) {
208 gc();
209 }
210
211 return mSize;
212 }
213
214 /**
215 * Given an index in the range <code>0...size()-1</code>, returns
216 * the key from the <code>index</code>th key-value mapping that this
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800217 * LongSparseArray stores.
Romain Guyfdbf6a72009-06-18 15:13:40 -0700218 */
219 public long keyAt(int index) {
220 if (mGarbage) {
221 gc();
222 }
223
224 return mKeys[index];
225 }
226
227 /**
228 * Given an index in the range <code>0...size()-1</code>, returns
229 * the value from the <code>index</code>th key-value mapping that this
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800230 * LongSparseArray stores.
Romain Guyfdbf6a72009-06-18 15:13:40 -0700231 */
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800232 @SuppressWarnings("unchecked")
Romain Guyfdbf6a72009-06-18 15:13:40 -0700233 public E valueAt(int index) {
234 if (mGarbage) {
235 gc();
236 }
237
238 return (E) mValues[index];
239 }
240
241 /**
242 * Given an index in the range <code>0...size()-1</code>, sets a new
243 * value for the <code>index</code>th key-value mapping that this
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800244 * LongSparseArray stores.
Romain Guyfdbf6a72009-06-18 15:13:40 -0700245 */
246 public void setValueAt(int index, E value) {
247 if (mGarbage) {
248 gc();
249 }
250
251 mValues[index] = value;
252 }
253
254 /**
255 * Returns the index for which {@link #keyAt} would return the
256 * specified key, or a negative number if the specified
257 * key is not mapped.
258 */
259 public int indexOfKey(long key) {
260 if (mGarbage) {
261 gc();
262 }
263
264 return binarySearch(mKeys, 0, mSize, key);
265 }
266
267 /**
268 * Returns an index for which {@link #valueAt} would return the
269 * specified key, or a negative number if no keys map to the
270 * specified value.
271 * Beware that this is a linear search, unlike lookups by key,
272 * and that multiple keys can map to the same value and this will
273 * find only one of them.
274 */
275 public int indexOfValue(E value) {
276 if (mGarbage) {
277 gc();
278 }
279
280 for (int i = 0; i < mSize; i++)
281 if (mValues[i] == value)
282 return i;
283
284 return -1;
285 }
286
287 /**
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800288 * Removes all key-value mappings from this LongSparseArray.
Romain Guyfdbf6a72009-06-18 15:13:40 -0700289 */
290 public void clear() {
291 int n = mSize;
292 Object[] values = mValues;
293
294 for (int i = 0; i < n; i++) {
295 values[i] = null;
296 }
297
298 mSize = 0;
299 mGarbage = false;
300 }
301
302 /**
303 * Puts a key/value pair into the array, optimizing for the case where
304 * the key is greater than all existing keys in the array.
305 */
306 public void append(long key, E value) {
307 if (mSize != 0 && key <= mKeys[mSize - 1]) {
308 put(key, value);
309 return;
310 }
311
312 if (mGarbage && mSize >= mKeys.length) {
313 gc();
314 }
315
316 int pos = mSize;
317 if (pos >= mKeys.length) {
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800318 int n = ArrayUtils.idealLongArraySize(pos + 1);
Romain Guyfdbf6a72009-06-18 15:13:40 -0700319
320 long[] nkeys = new long[n];
321 Object[] nvalues = new Object[n];
322
323 // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
324 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
325 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
326
327 mKeys = nkeys;
328 mValues = nvalues;
329 }
330
331 mKeys[pos] = key;
332 mValues[pos] = value;
333 mSize = pos + 1;
334 }
335
336 private static int binarySearch(long[] a, int start, int len, long key) {
337 int high = start + len, low = start - 1, guess;
338
339 while (high - low > 1) {
340 guess = (high + low) / 2;
341
342 if (a[guess] < key)
343 low = guess;
344 else
345 high = guess;
346 }
347
348 if (high == start + len)
349 return ~(start + len);
350 else if (a[high] == key)
351 return high;
352 else
353 return ~high;
354 }
Dianne Hackborn9c1d2982012-02-10 12:36:18 -0800355}