SparseIntArray.java revision ffe82bdcb5c914b3a60b630c6d3abe6fc9229dec
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
32package 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 */
39public class SparseIntArray {
40    /**
41     * Creates a new SparseIntArray containing no mappings.
42     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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