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