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