Home | History | Annotate | Download | only in util
      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      */
     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      */
     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      */
     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      */
     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      */
     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      */
     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      */
     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      */
    113     public void remove(long key) {
    114         delete(key);
    115     }
    116 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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 }