• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013, Google Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 package org.jf.util;
33 
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.List;
37 
38 /**
39  * SparseArrays map integers to Objects.  Unlike a normal array of Objects,
40  * there can be gaps in the indices.  It is intended to be more efficient
41  * than using a HashMap to map Integers to Objects.
42  */
43 public class SparseArray<E> {
44     private static final Object DELETED = new Object();
45     private boolean mGarbage = false;
46 
47     /**
48      * Creates a new SparseArray containing no mappings.
49      */
SparseArray()50     public SparseArray() {
51         this(10);
52     }
53 
54     /**
55      * Creates a new SparseArray containing no mappings that will not
56      * require any additional memory allocation to store the specified
57      * number of mappings.
58      */
SparseArray(int initialCapacity)59     public SparseArray(int initialCapacity) {
60         mKeys = new int[initialCapacity];
61         mValues = new Object[initialCapacity];
62         mSize = 0;
63     }
64 
65     /**
66      * Gets the Object mapped from the specified key, or <code>null</code>
67      * if no such mapping has been made.
68      */
get(int key)69     public E get(int key) {
70         return get(key, null);
71     }
72 
73     /**
74      * Gets the Object mapped from the specified key, or the specified Object
75      * if no such mapping has been made.
76      */
get(int key, E valueIfKeyNotFound)77     public E get(int key, E valueIfKeyNotFound) {
78         int i = binarySearch(mKeys, 0, mSize, key);
79 
80         if (i < 0 || mValues[i] == DELETED) {
81             return valueIfKeyNotFound;
82         } else {
83             return (E) mValues[i];
84         }
85     }
86 
87     /**
88      * Removes the mapping from the specified key, if there was any.
89      */
delete(int key)90     public void delete(int key) {
91         int i = binarySearch(mKeys, 0, mSize, key);
92 
93         if (i >= 0) {
94             if (mValues[i] != DELETED) {
95                 mValues[i] = DELETED;
96                 mGarbage = true;
97             }
98         }
99     }
100 
101     /**
102      * Alias for {@link #delete(int)}.
103      */
remove(int key)104     public void remove(int key) {
105         delete(key);
106     }
107 
gc()108     private void gc() {
109         // Log.e("SparseArray", "gc start with " + mSize);
110 
111         int n = mSize;
112         int o = 0;
113         int[] keys = mKeys;
114         Object[] values = mValues;
115 
116         for (int i = 0; i < n; i++) {
117             Object val = values[i];
118 
119             if (val != DELETED) {
120                 if (i != o) {
121                     keys[o] = keys[i];
122                     values[o] = val;
123                 }
124 
125                 o++;
126             }
127         }
128 
129         mGarbage = false;
130         mSize = o;
131 
132         // Log.e("SparseArray", "gc end with " + mSize);
133     }
134 
135     /**
136      * Adds a mapping from the specified key to the specified value,
137      * replacing the previous mapping from the specified key if there
138      * was one.
139      */
put(int key, E value)140     public void put(int key, E value) {
141         int i = binarySearch(mKeys, 0, mSize, key);
142 
143         if (i >= 0) {
144             mValues[i] = value;
145         } else {
146             i = ~i;
147 
148             if (i < mSize && mValues[i] == DELETED) {
149                 mKeys[i] = key;
150                 mValues[i] = value;
151                 return;
152             }
153 
154             if (mGarbage && mSize >= mKeys.length) {
155                 gc();
156 
157                 // Search again because indices may have changed.
158                 i = ~binarySearch(mKeys, 0, mSize, key);
159             }
160 
161             if (mSize >= mKeys.length) {
162                 int n = Math.max(mSize + 1, mKeys.length * 2);
163 
164                 int[] nkeys = new int[n];
165                 Object[] nvalues = new Object[n];
166 
167                 // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
168                 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
169                 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
170 
171                 mKeys = nkeys;
172                 mValues = nvalues;
173             }
174 
175             if (mSize - i != 0) {
176                 // Log.e("SparseArray", "move " + (mSize - i));
177                 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
178                 System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
179             }
180 
181             mKeys[i] = key;
182             mValues[i] = value;
183             mSize++;
184         }
185     }
186 
187     /**
188      * Returns the number of key-value mappings that this SparseArray
189      * currently stores.
190      */
size()191     public int size() {
192         if (mGarbage) {
193             gc();
194         }
195 
196         return mSize;
197     }
198 
199     /**
200      * Given an index in the range <code>0...size()-1</code>, returns
201      * the key from the <code>index</code>th key-value mapping that this
202      * SparseArray stores.
203      */
keyAt(int index)204     public int keyAt(int index) {
205         if (mGarbage) {
206             gc();
207         }
208 
209         return mKeys[index];
210     }
211 
212     /**
213      * Given an index in the range <code>0...size()-1</code>, returns
214      * the value from the <code>index</code>th key-value mapping that this
215      * SparseArray stores.
216      */
valueAt(int index)217     public E valueAt(int index) {
218         if (mGarbage) {
219             gc();
220         }
221 
222         return (E) mValues[index];
223     }
224 
225     /**
226      * Given an index in the range <code>0...size()-1</code>, sets a new
227      * value for the <code>index</code>th key-value mapping that this
228      * SparseArray stores.
229      */
setValueAt(int index, E value)230     public void setValueAt(int index, E value) {
231         if (mGarbage) {
232             gc();
233         }
234 
235         mValues[index] = value;
236     }
237 
238     /**
239      * Returns the index for which {@link #keyAt} would return the
240      * specified key, or a negative number if the specified
241      * key is not mapped.
242      */
indexOfKey(int key)243     public int indexOfKey(int key) {
244         if (mGarbage) {
245             gc();
246         }
247 
248         return binarySearch(mKeys, 0, mSize, key);
249     }
250 
251     /**
252      * Returns an index for which {@link #valueAt} would return the
253      * specified key, or a negative number if no keys map to the
254      * specified value.
255      * Beware that this is a linear search, unlike lookups by key,
256      * and that multiple keys can map to the same value and this will
257      * find only one of them.
258      */
indexOfValue(E value)259     public int indexOfValue(E value) {
260         if (mGarbage) {
261             gc();
262         }
263 
264         for (int i = 0; i < mSize; i++)
265             if (mValues[i] == value)
266                 return i;
267 
268         return -1;
269     }
270 
271     /**
272      * Removes all key-value mappings from this SparseArray.
273      */
clear()274     public void clear() {
275         int n = mSize;
276         Object[] values = mValues;
277 
278         for (int i = 0; i < n; i++) {
279             values[i] = null;
280         }
281 
282         mSize = 0;
283         mGarbage = false;
284     }
285 
286     /**
287      * Puts a key/value pair into the array, optimizing for the case where
288      * the key is greater than all existing keys in the array.
289      */
append(int key, E value)290     public void append(int key, E value) {
291         if (mSize != 0 && key <= mKeys[mSize - 1]) {
292             put(key, value);
293             return;
294         }
295 
296         if (mGarbage && mSize >= mKeys.length) {
297             gc();
298         }
299 
300         int pos = mSize;
301         if (pos >= mKeys.length) {
302             int n = Math.max(pos + 1, mKeys.length * 2);
303 
304             int[] nkeys = new int[n];
305             Object[] nvalues = new Object[n];
306 
307             // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
308             System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
309             System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
310 
311             mKeys = nkeys;
312             mValues = nvalues;
313         }
314 
315         mKeys[pos] = key;
316         mValues[pos] = value;
317         mSize = pos + 1;
318     }
319 
320     /**
321      * Increases the size of the underlying storage if needed, to ensure that it can
322      * hold the specified number of items without having to allocate additional memory
323      * @param capacity the number of items
324      */
ensureCapacity(int capacity)325     public void ensureCapacity(int capacity) {
326         if (mGarbage && mSize >= mKeys.length) {
327             gc();
328         }
329 
330         if (mKeys.length < capacity) {
331             int[] nkeys = new int[capacity];
332             Object[] nvalues = new Object[capacity];
333 
334             System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
335             System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
336 
337             mKeys = nkeys;
338             mValues = nvalues;
339         }
340     }
341 
binarySearch(int[] a, int start, int len, int key)342     private static int binarySearch(int[] a, int start, int len, int key) {
343         int high = start + len, low = start - 1, guess;
344 
345         while (high - low > 1) {
346             guess = (high + low) / 2;
347 
348             if (a[guess] < key)
349                 low = guess;
350             else
351                 high = guess;
352         }
353 
354         if (high == start + len)
355             return ~(start + len);
356         else if (a[high] == key)
357             return high;
358         else
359             return ~high;
360     }
361 
362     /**
363      * @return a read-only list of the values in this SparseArray which are in ascending order, based on their
364      * associated key
365      */
getValues()366     public List<E> getValues() {
367         return Collections.unmodifiableList(Arrays.asList((E[])mValues));
368     }
369 
370     private int[] mKeys;
371     private Object[] mValues;
372     private int mSize;
373 }
374