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 import com.android.internal.util.GrowingArrayUtils; 21 22 import libcore.util.EmptyArray; 23 24 /** 25 * SparseBooleanArrays map integers to booleans. 26 * Unlike a normal array of booleans 27 * there can be gaps in the indices. It is intended to be more memory efficient 28 * than using a HashMap to map Integers to Booleans, both because it avoids 29 * auto-boxing keys and values and its data structure doesn't rely on an extra entry object 30 * for each mapping. 31 * 32 * <p>Note that this container keeps its mappings in an array data structure, 33 * using a binary search to find keys. The implementation is not intended to be appropriate for 34 * data structures 35 * that may contain large numbers of items. It is generally slower than a traditional 36 * HashMap, since lookups require a binary search and adds and removes require inserting 37 * and deleting entries in the array. For containers holding up to hundreds of items, 38 * the performance difference is not significant, less than 50%.</p> 39 * 40 * <p>It is possible to iterate over the items in this container using 41 * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using 42 * <code>keyAt(int)</code> with ascending values of the index will return the 43 * keys in ascending order, or the values corresponding to the keys in ascending 44 * order in the case of <code>valueAt(int)</code>.</p> 45 */ 46 public class SparseBooleanArray implements Cloneable { 47 /** 48 * Creates a new SparseBooleanArray containing no mappings. 49 */ SparseBooleanArray()50 public SparseBooleanArray() { 51 this(10); 52 } 53 54 /** 55 * Creates a new SparseBooleanArray containing no mappings that will not 56 * require any additional memory allocation to store the specified 57 * number of mappings. If you supply an initial capacity of 0, the 58 * sparse array will be initialized with a light-weight representation 59 * not requiring any additional array allocations. 60 */ SparseBooleanArray(int initialCapacity)61 public SparseBooleanArray(int initialCapacity) { 62 if (initialCapacity == 0) { 63 mKeys = EmptyArray.INT; 64 mValues = EmptyArray.BOOLEAN; 65 } else { 66 mKeys = ArrayUtils.newUnpaddedIntArray(initialCapacity); 67 mValues = new boolean[mKeys.length]; 68 } 69 mSize = 0; 70 } 71 72 @Override clone()73 public SparseBooleanArray clone() { 74 SparseBooleanArray clone = null; 75 try { 76 clone = (SparseBooleanArray) super.clone(); 77 clone.mKeys = mKeys.clone(); 78 clone.mValues = mValues.clone(); 79 } catch (CloneNotSupportedException cnse) { 80 /* ignore */ 81 } 82 return clone; 83 } 84 85 /** 86 * Gets the boolean mapped from the specified key, or <code>false</code> 87 * if no such mapping has been made. 88 */ get(int key)89 public boolean get(int key) { 90 return get(key, false); 91 } 92 93 /** 94 * Gets the boolean mapped from the specified key, or the specified value 95 * if no such mapping has been made. 96 */ get(int key, boolean valueIfKeyNotFound)97 public boolean get(int key, boolean valueIfKeyNotFound) { 98 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 99 100 if (i < 0) { 101 return valueIfKeyNotFound; 102 } else { 103 return mValues[i]; 104 } 105 } 106 107 /** 108 * Removes the mapping from the specified key, if there was any. 109 */ delete(int key)110 public void delete(int key) { 111 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 112 113 if (i >= 0) { 114 System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1)); 115 System.arraycopy(mValues, i + 1, mValues, i, mSize - (i + 1)); 116 mSize--; 117 } 118 } 119 120 /** @hide */ removeAt(int index)121 public void removeAt(int index) { 122 System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1)); 123 System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1)); 124 mSize--; 125 } 126 127 /** 128 * Adds a mapping from the specified key to the specified value, 129 * replacing the previous mapping from the specified key if there 130 * was one. 131 */ put(int key, boolean value)132 public void put(int key, boolean value) { 133 int i = ContainerHelpers.binarySearch(mKeys, mSize, key); 134 135 if (i >= 0) { 136 mValues[i] = value; 137 } else { 138 i = ~i; 139 140 mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); 141 mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); 142 mSize++; 143 } 144 } 145 146 /** 147 * Returns the number of key-value mappings that this SparseBooleanArray 148 * currently stores. 149 */ size()150 public int size() { 151 return mSize; 152 } 153 154 /** 155 * Given an index in the range <code>0...size()-1</code>, returns 156 * the key from the <code>index</code>th key-value mapping that this 157 * SparseBooleanArray stores. 158 * 159 * <p>The keys corresponding to indices in ascending order are guaranteed to 160 * be in ascending order, e.g., <code>keyAt(0)</code> will return the 161 * smallest key and <code>keyAt(size()-1)</code> will return the largest 162 * key.</p> 163 */ keyAt(int index)164 public int keyAt(int index) { 165 return mKeys[index]; 166 } 167 168 /** 169 * Given an index in the range <code>0...size()-1</code>, returns 170 * the value from the <code>index</code>th key-value mapping that this 171 * SparseBooleanArray stores. 172 * 173 * <p>The values corresponding to indices in ascending order are guaranteed 174 * to be associated with keys in ascending order, e.g., 175 * <code>valueAt(0)</code> will return the value associated with the 176 * smallest key and <code>valueAt(size()-1)</code> will return the value 177 * associated with the largest key.</p> 178 */ valueAt(int index)179 public boolean valueAt(int index) { 180 return mValues[index]; 181 } 182 183 /** @hide */ setValueAt(int index, boolean value)184 public void setValueAt(int index, boolean value) { 185 mValues[index] = value; 186 } 187 188 /** 189 * Returns the index for which {@link #keyAt} would return the 190 * specified key, or a negative number if the specified 191 * key is not mapped. 192 */ indexOfKey(int key)193 public int indexOfKey(int key) { 194 return ContainerHelpers.binarySearch(mKeys, mSize, key); 195 } 196 197 /** 198 * Returns an index for which {@link #valueAt} would return the 199 * specified key, or a negative number if no keys map to the 200 * specified value. 201 * Beware that this is a linear search, unlike lookups by key, 202 * and that multiple keys can map to the same value and this will 203 * find only one of them. 204 */ indexOfValue(boolean value)205 public int indexOfValue(boolean value) { 206 for (int i = 0; i < mSize; i++) 207 if (mValues[i] == value) 208 return i; 209 210 return -1; 211 } 212 213 /** 214 * Removes all key-value mappings from this SparseBooleanArray. 215 */ clear()216 public void clear() { 217 mSize = 0; 218 } 219 220 /** 221 * Puts a key/value pair into the array, optimizing for the case where 222 * the key is greater than all existing keys in the array. 223 */ append(int key, boolean value)224 public void append(int key, boolean value) { 225 if (mSize != 0 && key <= mKeys[mSize - 1]) { 226 put(key, value); 227 return; 228 } 229 230 mKeys = GrowingArrayUtils.append(mKeys, mSize, key); 231 mValues = GrowingArrayUtils.append(mValues, mSize, value); 232 mSize++; 233 } 234 235 /** 236 * {@inheritDoc} 237 * 238 * <p>This implementation composes a string by iterating over its mappings. 239 */ 240 @Override toString()241 public String toString() { 242 if (size() <= 0) { 243 return "{}"; 244 } 245 246 StringBuilder buffer = new StringBuilder(mSize * 28); 247 buffer.append('{'); 248 for (int i=0; i<mSize; i++) { 249 if (i > 0) { 250 buffer.append(", "); 251 } 252 int key = keyAt(i); 253 buffer.append(key); 254 buffer.append('='); 255 boolean value = valueAt(i); 256 buffer.append(value); 257 } 258 buffer.append('}'); 259 return buffer.toString(); 260 } 261 262 private int[] mKeys; 263 private boolean[] mValues; 264 private int mSize; 265 } 266