From f4bf0ae2a7c2d9d92c5c8abdb82baa53b4c9ccda Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 20 May 2013 18:42:16 -0700 Subject: New ArrayMap class. This is a new kind of key/value mapping that stores its data as an array, so it doesn't need to create an extra Entry object for every mapping placed in to it. It is also optimized to reduce memory overhead in other ways, by keeping the base object small, being fairly aggressive about keeping the array data structures small, etc. There are some unit and performance tests dropped in to some random places; they will need to be put somewhere else once I decided what we are going to do with this for the next release (for example if we make it public the unit tests should go in to CTS). Switch IntentResolver to using ArrayMap instead of HashMap. Also get rid of a bunch of duplicate implementations of binarySearch, and add an optimization to the various sparse arrays where you can supply an explicit 0 capacity to prevent it from doing an initial array allocation; use this new optimization in a few places where it makes sense. Change-Id: I01ef2764680f8ae49938e2a2ed40dc01606a056b --- core/java/android/util/SparseBooleanArray.java | 46 ++++++++++---------------- 1 file changed, 17 insertions(+), 29 deletions(-) (limited to 'core/java/android/util/SparseBooleanArray.java') diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 76c47c6aabad..73e3629d467d 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -25,6 +25,8 @@ import com.android.internal.util.ArrayUtils; * than using a HashMap to map Integers to Booleans. */ public class SparseBooleanArray implements Cloneable { + static final boolean[] EMPTY_BOOLEANS = new boolean[0]; + /** * Creates a new SparseBooleanArray containing no mappings. */ @@ -35,13 +37,19 @@ public class SparseBooleanArray implements Cloneable { /** * Creates a new SparseBooleanArray containing no mappings that will not * require any additional memory allocation to store the specified - * number of mappings. + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. */ public SparseBooleanArray(int initialCapacity) { - initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); - - mKeys = new int[initialCapacity]; - mValues = new boolean[initialCapacity]; + if (initialCapacity == 0) { + mKeys = SparseArray.EMPTY_INTS; + mValues = EMPTY_BOOLEANS; + } else { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + mKeys = new int[initialCapacity]; + mValues = new boolean[initialCapacity]; + } mSize = 0; } @@ -71,7 +79,7 @@ public class SparseBooleanArray implements Cloneable { * if no such mapping has been made. */ public boolean get(int key, boolean valueIfKeyNotFound) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i < 0) { return valueIfKeyNotFound; @@ -84,7 +92,7 @@ public class SparseBooleanArray implements Cloneable { * Removes the mapping from the specified key, if there was any. */ public void delete(int key) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i >= 0) { System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1)); @@ -99,7 +107,7 @@ public class SparseBooleanArray implements Cloneable { * was one. */ public void put(int key, boolean value) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i >= 0) { mValues[i] = value; @@ -164,7 +172,7 @@ public class SparseBooleanArray implements Cloneable { * key is not mapped. */ public int indexOfKey(int key) { - return binarySearch(mKeys, 0, mSize, key); + return SparseArray.binarySearch(mKeys, mSize, key); } /** @@ -220,26 +228,6 @@ public class SparseBooleanArray implements Cloneable { mSize = pos + 1; } - private static int binarySearch(int[] a, int start, int len, int key) { - int high = start + len, low = start - 1, guess; - - while (high - low > 1) { - guess = (high + low) / 2; - - if (a[guess] < key) - low = guess; - else - high = guess; - } - - if (high == start + len) - return ~(start + len); - else if (a[high] == key) - return high; - else - return ~high; - } - private int[] mKeys; private boolean[] mValues; private int mSize; -- cgit v1.2.3 From b993f41eb2f165425dfdf0f93ea0b1e354eca837 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 12 Jul 2013 17:46:45 -0700 Subject: Update SparseArray docs to be more informative. Change-Id: I5d8d17d46a69ccdcf6b29f93be3d44addd80ab61 --- core/java/android/util/SparseBooleanArray.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'core/java/android/util/SparseBooleanArray.java') diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 73e3629d467d..430755a94e41 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -21,8 +21,18 @@ import com.android.internal.util.ArrayUtils; /** * SparseBooleanArrays map integers to booleans. * Unlike a normal array of booleans - * there can be gaps in the indices. It is intended to be more efficient - * than using a HashMap to map Integers to Booleans. + * there can be gaps in the indices. It is intended to be more memory efficient + * than using a HashMap to map Integers to Booleans, both because it avoids + * auto-boxing keys and values and its data structure doesn't rely on an extra entry object + * for each mapping. + * + *

Note that this container keeps its mappings in an array data structure, + * using a binary search to find keys. The implementation is not intended to be appropriate for + * data structures + * that may contain large numbers of items. It is generally slower than a traditional + * HashMap, since lookups require a binary search and adds and removes require inserting + * and deleting entries in the array. For containers holding up to hundreds of items, + * the performance difference is not significant, less than 50%.

*/ public class SparseBooleanArray implements Cloneable { static final boolean[] EMPTY_BOOLEANS = new boolean[0]; -- cgit v1.2.3 From 3e82ba1a67b0c756ab6a289985f4cfc53725b311 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 16 Jul 2013 13:23:55 -0700 Subject: Make ArrayMap public! :) Also do some tweaking of the various container classes to synchronize them with the support lib and make it easier to copy code between the two. And update activity/fragment to use ArrayMap. Change-Id: I3cfe82392a17119dfc72c3d9961f64e1914f42be --- core/java/android/util/SparseBooleanArray.java | 43 ++++++++++++++++++++------ 1 file changed, 34 insertions(+), 9 deletions(-) (limited to 'core/java/android/util/SparseBooleanArray.java') diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 430755a94e41..da196d78161d 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -35,8 +35,6 @@ import com.android.internal.util.ArrayUtils; * the performance difference is not significant, less than 50%.

*/ public class SparseBooleanArray implements Cloneable { - static final boolean[] EMPTY_BOOLEANS = new boolean[0]; - /** * Creates a new SparseBooleanArray containing no mappings. */ @@ -53,8 +51,8 @@ public class SparseBooleanArray implements Cloneable { */ public SparseBooleanArray(int initialCapacity) { if (initialCapacity == 0) { - mKeys = SparseArray.EMPTY_INTS; - mValues = EMPTY_BOOLEANS; + mKeys = ContainerHelpers.EMPTY_INTS; + mValues = ContainerHelpers.EMPTY_BOOLEANS; } else { initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); mKeys = new int[initialCapacity]; @@ -89,7 +87,7 @@ public class SparseBooleanArray implements Cloneable { * if no such mapping has been made. */ public boolean get(int key, boolean valueIfKeyNotFound) { - int i = SparseArray.binarySearch(mKeys, mSize, key); + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); if (i < 0) { return valueIfKeyNotFound; @@ -102,7 +100,7 @@ public class SparseBooleanArray implements Cloneable { * Removes the mapping from the specified key, if there was any. */ public void delete(int key) { - int i = SparseArray.binarySearch(mKeys, mSize, key); + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); if (i >= 0) { System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1)); @@ -117,7 +115,7 @@ public class SparseBooleanArray implements Cloneable { * was one. */ public void put(int key, boolean value) { - int i = SparseArray.binarySearch(mKeys, mSize, key); + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); if (i >= 0) { mValues[i] = value; @@ -182,7 +180,7 @@ public class SparseBooleanArray implements Cloneable { * key is not mapped. */ public int indexOfKey(int key) { - return SparseArray.binarySearch(mKeys, mSize, key); + return ContainerHelpers.binarySearch(mKeys, mSize, key); } /** @@ -237,7 +235,34 @@ public class SparseBooleanArray implements Cloneable { mValues[pos] = value; mSize = pos + 1; } - + + /** + * {@inheritDoc} + * + *

This implementation composes a string by iterating over its mappings. + */ + @Override + public String toString() { + if (size() <= 0) { + return "{}"; + } + + StringBuilder buffer = new StringBuilder(mSize * 28); + buffer.append('{'); + for (int i=0; i 0) { + buffer.append(", "); + } + int key = keyAt(i); + buffer.append(key); + buffer.append('='); + boolean value = valueAt(i); + buffer.append(value); + } + buffer.append('}'); + return buffer.toString(); + } + private int[] mKeys; private boolean[] mValues; private int mSize; -- cgit v1.2.3 From 5771302ca13c31cb85f17bc58da8f6f8227c9b85 Mon Sep 17 00:00:00 2001 From: Flavio Lerda Date: Sun, 8 Sep 2013 18:04:58 +0100 Subject: Document the order of values returned by keyAt(). The values returned by keyAt() are currently guaranteed to be in ascending order. This is helpful to users of the API to be able to make assumptions about the keys and values when iterating over one of the sparse array implementations. This commit adds documentation about this. Change-Id: I3d7eb78e115ce174f1167b83904b44bf5120b223 --- core/java/android/util/SparseBooleanArray.java | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'core/java/android/util/SparseBooleanArray.java') diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index da196d78161d..68487e391710 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -33,6 +33,12 @@ import com.android.internal.util.ArrayUtils; * HashMap, since lookups require a binary search and adds and removes require inserting * and deleting entries in the array. For containers holding up to hundreds of items, * the performance difference is not significant, less than 50%.

+ * + *

It is possible to iterate over the items in this container using + * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using + * keyAt(int) with ascending values of the index will return the + * keys in ascending order, or the values corresponding to the keys in ascending + * order in the case of valueAt(int).

*/ public class SparseBooleanArray implements Cloneable { /** @@ -159,16 +165,27 @@ public class SparseBooleanArray implements Cloneable { /** * Given an index in the range 0...size()-1, returns * the key from the indexth key-value mapping that this - * SparseBooleanArray stores. + * SparseBooleanArray stores. + * + *

The keys corresponding to indices in ascending order are guaranteed to + * be in ascending order, e.g., keyAt(0) will return the + * smallest key and keyAt(size()-1) will return the largest + * key.

*/ public int keyAt(int index) { return mKeys[index]; } - + /** * Given an index in the range 0...size()-1, returns * the value from the indexth key-value mapping that this - * SparseBooleanArray stores. + * SparseBooleanArray stores. + * + *

The values corresponding to indices in ascending order are guaranteed + * to be associated with keys in ascending order, e.g., + * valueAt(0) will return the value associated with the + * smallest key and valueAt(size()-1) will return the value + * associated with the largest key.

*/ public boolean valueAt(int index) { return mValues[index]; -- cgit v1.2.3