Home | History | Annotate | Download | only in util
      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  * SparseBooleanArrays map integers to booleans.
     23  * Unlike a normal array of booleans
     24  * there can be gaps in the indices.  It is intended to be more efficient
     25  * than using a HashMap to map Integers to Booleans.
     26  */
     27 public class SparseBooleanArray {
     28     /**
     29      * Creates a new SparseBooleanArray containing no mappings.
     30      */
     31     public SparseBooleanArray() {
     32         this(10);
     33     }
     34 
     35     /**
     36      * Creates a new SparseBooleanArray containing no mappings that will not
     37      * require any additional memory allocation to store the specified
     38      * number of mappings.
     39      */
     40     public SparseBooleanArray(int initialCapacity) {
     41         initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
     42 
     43         mKeys = new int[initialCapacity];
     44         mValues = new boolean[initialCapacity];
     45         mSize = 0;
     46     }
     47 
     48     /**
     49      * Gets the boolean mapped from the specified key, or <code>false</code>
     50      * if no such mapping has been made.
     51      */
     52     public boolean get(int key) {
     53         return get(key, false);
     54     }
     55 
     56     /**
     57      * Gets the boolean mapped from the specified key, or the specified value
     58      * if no such mapping has been made.
     59      */
     60     public boolean get(int key, boolean valueIfKeyNotFound) {
     61         int i = binarySearch(mKeys, 0, mSize, key);
     62 
     63         if (i < 0) {
     64             return valueIfKeyNotFound;
     65         } else {
     66             return mValues[i];
     67         }
     68     }
     69 
     70     /**
     71      * Removes the mapping from the specified key, if there was any.
     72      */
     73     public void delete(int key) {
     74         int i = binarySearch(mKeys, 0, mSize, key);
     75 
     76         if (i >= 0) {
     77             System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1));
     78             System.arraycopy(mValues, i + 1, mValues, i, mSize - (i + 1));
     79             mSize--;
     80         }
     81     }
     82 
     83     /**
     84      * Adds a mapping from the specified key to the specified value,
     85      * replacing the previous mapping from the specified key if there
     86      * was one.
     87      */
     88     public void put(int key, boolean value) {
     89         int i = binarySearch(mKeys, 0, mSize, key);
     90 
     91         if (i >= 0) {
     92             mValues[i] = value;
     93         } else {
     94             i = ~i;
     95 
     96             if (mSize >= mKeys.length) {
     97                 int n = ArrayUtils.idealIntArraySize(mSize + 1);
     98 
     99                 int[] nkeys = new int[n];
    100                 boolean[] nvalues = new boolean[n];
    101 
    102                 // Log.e("SparseBooleanArray", "grow " + mKeys.length + " to " + n);
    103                 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
    104                 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
    105 
    106                 mKeys = nkeys;
    107                 mValues = nvalues;
    108             }
    109 
    110             if (mSize - i != 0) {
    111                 // Log.e("SparseBooleanArray", "move " + (mSize - i));
    112                 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
    113                 System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
    114             }
    115 
    116             mKeys[i] = key;
    117             mValues[i] = value;
    118             mSize++;
    119         }
    120     }
    121 
    122     /**
    123      * Returns the number of key-value mappings that this SparseBooleanArray
    124      * currently stores.
    125      */
    126     public int size() {
    127         return mSize;
    128     }
    129 
    130     /**
    131      * Given an index in the range <code>0...size()-1</code>, returns
    132      * the key from the <code>index</code>th key-value mapping that this
    133      * SparseBooleanArray stores.
    134      */
    135     public int keyAt(int index) {
    136         return mKeys[index];
    137     }
    138 
    139     /**
    140      * Given an index in the range <code>0...size()-1</code>, returns
    141      * the value from the <code>index</code>th key-value mapping that this
    142      * SparseBooleanArray stores.
    143      */
    144     public boolean valueAt(int index) {
    145         return mValues[index];
    146     }
    147 
    148     /**
    149      * Returns the index for which {@link #keyAt} would return the
    150      * specified key, or a negative number if the specified
    151      * key is not mapped.
    152      */
    153     public int indexOfKey(int key) {
    154         return binarySearch(mKeys, 0, mSize, key);
    155     }
    156 
    157     /**
    158      * Returns an index for which {@link #valueAt} would return the
    159      * specified key, or a negative number if no keys map to the
    160      * specified value.
    161      * Beware that this is a linear search, unlike lookups by key,
    162      * and that multiple keys can map to the same value and this will
    163      * find only one of them.
    164      */
    165     public int indexOfValue(boolean value) {
    166         for (int i = 0; i < mSize; i++)
    167             if (mValues[i] == value)
    168                 return i;
    169 
    170         return -1;
    171     }
    172 
    173     /**
    174      * Removes all key-value mappings from this SparseBooleanArray.
    175      */
    176     public void clear() {
    177         mSize = 0;
    178     }
    179 
    180     /**
    181      * Puts a key/value pair into the array, optimizing for the case where
    182      * the key is greater than all existing keys in the array.
    183      */
    184     public void append(int key, boolean value) {
    185         if (mSize != 0 && key <= mKeys[mSize - 1]) {
    186             put(key, value);
    187             return;
    188         }
    189 
    190         int pos = mSize;
    191         if (pos >= mKeys.length) {
    192             int n = ArrayUtils.idealIntArraySize(pos + 1);
    193 
    194             int[] nkeys = new int[n];
    195             boolean[] nvalues = new boolean[n];
    196 
    197             // Log.e("SparseBooleanArray", "grow " + mKeys.length + " to " + n);
    198             System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
    199             System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
    200 
    201             mKeys = nkeys;
    202             mValues = nvalues;
    203         }
    204 
    205         mKeys[pos] = key;
    206         mValues[pos] = value;
    207         mSize = pos + 1;
    208     }
    209 
    210     private static int binarySearch(int[] a, int start, int len, int key) {
    211         int high = start + len, low = start - 1, guess;
    212 
    213         while (high - low > 1) {
    214             guess = (high + low) / 2;
    215 
    216             if (a[guess] < key)
    217                 low = guess;
    218             else
    219                 high = guess;
    220         }
    221 
    222         if (high == start + len)
    223             return ~(start + len);
    224         else if (a[high] == key)
    225             return high;
    226         else
    227             return ~high;
    228     }
    229 
    230     private void checkIntegrity() {
    231         for (int i = 1; i < mSize; i++) {
    232             if (mKeys[i] <= mKeys[i - 1]) {
    233                 for (int j = 0; j < mSize; j++) {
    234                     Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]);
    235                 }
    236 
    237                 throw new RuntimeException();
    238             }
    239         }
    240     }
    241 
    242     private int[] mKeys;
    243     private boolean[] mValues;
    244     private int mSize;
    245 }
    246