Loading core/java/android/util/SparseDoubleArray.java 0 → 100644 +166 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util; /** * SparseDoubleArrays map integers to doubles. Unlike a normal array of doubles, * there can be gaps in the indices. It is intended to be more memory efficient * than using a HashMap to map Integers to Doubles, both because it avoids * auto-boxing keys and values and its data structure doesn't rely on an extra entry object * for each mapping. * * <p>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%.</p> * * <p>It is possible to iterate over the items in this container using * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> 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 <code>valueAt(int)</code>.</p> * * @see SparseLongArray * * @hide */ public class SparseDoubleArray implements Cloneable { /** * The int->double map, but storing the doubles as longs using * {@link Double#doubleToRawLongBits(double)}. */ private SparseLongArray mValues; /** Creates a new SparseDoubleArray containing no mappings. */ public SparseDoubleArray() { this(10); } /** * Creates a new SparseDoubleArray, containing no mappings, that will not * require any additional memory allocation to store the specified * 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 SparseDoubleArray(int initialCapacity) { mValues = new SparseLongArray(initialCapacity); } @Override public SparseDoubleArray clone() { SparseDoubleArray clone = null; try { clone = (SparseDoubleArray) super.clone(); clone.mValues = mValues.clone(); } catch (CloneNotSupportedException cnse) { /* ignore */ } return clone; } /** * Gets the double mapped from the specified key, or <code>0</code> * if no such mapping has been made. */ public double get(int key) { final int index = mValues.indexOfKey(key); if (index < 0) { return 0.0d; } return valueAt(index); } /** * Adds a mapping from the specified key to the specified value, * replacing the previous mapping from the specified key if there * was one. */ public void put(int key, double value) { mValues.put(key, Double.doubleToRawLongBits(value)); } /** * Adds a mapping from the specified key to the specified value, * <b>adding</b> its value to the previous mapping from the specified key if there * was one. * * <p>This differs from {@link #put} because instead of replacing any previous value, it adds * (in the numerical sense) to it. */ public void add(int key, double summand) { final double oldValue = get(key); put(key, oldValue + summand); } /** Returns the number of key-value mappings that this SparseDoubleArray currently stores. */ public int size() { return mValues.size(); } /** * Given an index in the range <code>0...size()-1</code>, returns * the key from the <code>index</code>th key-value mapping that this * SparseDoubleArray stores. * * @see SparseLongArray#keyAt(int) */ public int keyAt(int index) { return mValues.keyAt(index); } /** * Given an index in the range <code>0...size()-1</code>, returns * the value from the <code>index</code>th key-value mapping that this * SparseDoubleArray stores. * * @see SparseLongArray#valueAt(int) */ public double valueAt(int index) { return Double.longBitsToDouble(mValues.valueAt(index)); } /** * {@inheritDoc} * * <p>This implementation composes a string by iterating over its mappings. */ @Override public String toString() { if (size() <= 0) { return "{}"; } StringBuilder buffer = new StringBuilder(size() * 34); buffer.append('{'); for (int i = 0; i < size(); i++) { if (i > 0) { buffer.append(", "); } int key = keyAt(i); buffer.append(key); buffer.append('='); double value = valueAt(i); buffer.append(value); } buffer.append('}'); return buffer.toString(); } } core/java/com/android/internal/os/BatteryStatsImpl.java +1 −75 Original line number Diff line number Diff line Loading @@ -89,6 +89,7 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; import android.util.SparseArray; import android.util.SparseDoubleArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeUtils; Loading Loading @@ -12547,81 +12548,6 @@ public class BatteryStatsImpl extends BatteryStats { } } /** * SparseDoubleArray map integers to doubles. * Its implementation is the same as that of {@link SparseLongArray}; see there for details. * * @see SparseLongArray */ private static class SparseDoubleArray { /** * The int->double map, but storing the doubles as longs using * {@link Double.doubleToRawLongBits(double)}. */ private final SparseLongArray mValues = new SparseLongArray(); /** * Gets the double mapped from the specified key, or <code>0</code> * if no such mapping has been made. */ public double get(int key) { if (mValues.indexOfKey(key) >= 0) { return Double.longBitsToDouble(mValues.get(key)); } return 0; } /** * Adds a mapping from the specified key to the specified value, * replacing the previous mapping from the specified key if there * was one. */ public void put(int key, double value) { mValues.put(key, Double.doubleToRawLongBits(value)); } /** * Adds a mapping from the specified key to the specified value, * <b>adding</b> to the previous mapping from the specified key if there * was one. */ public void add(int key, double summand) { final double oldValue = get(key); put(key, oldValue + summand); } /** * Returns the number of key-value mappings that this SparseDoubleArray * currently stores. */ public int size() { return mValues.size(); } /** * Given an index in the range <code>0...size()-1</code>, returns * the key from the <code>index</code>th key-value mapping that this * SparseDoubleArray stores. * * @see SparseLongArray#keyAt(int) */ public int keyAt(int index) { return mValues.keyAt(index); } /** * Given an index in the range <code>0...size()-1</code>, returns * the value from the <code>index</code>th key-value mapping that this * SparseDoubleArray stores. * * @see SparseLongArray#valueAt(int) */ public double valueAt(int index) { return Double.longBitsToDouble(mValues.valueAt(index)); } } /** * Read and record Rail Energy data. */ Loading core/tests/coretests/src/android/util/SparseDoubleArrayTest.java 0 → 100644 +108 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util; import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; /** * Internal tests for {@link SparseDoubleArray}. * * Run using: * atest FrameworksCoreTests:android.util.SparseDoubleArrayTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class SparseDoubleArrayTest { private static final double EXACT_PRECISION = 0; private static final double PRECISION = 0.000000001; @Test public void testPutGet() { final SparseDoubleArray sda = new SparseDoubleArray(); assertEquals("Array should be empty", 0, sda.size()); final int[] keys = {1, 6, -14, 53251, 5, -13412, 12, 0, 2}; final double[] values = {7, -12.4, 7, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, 4311236312.0 / 3431470161413514334123.0, 431123636434132151313412.0 / 34323.0, 0}; for (int i = 0; i < keys.length; i++) { sda.put(keys[i], values[i]); } assertEquals("Wrong size array", keys.length, sda.size()); // Due to the implementation, we actually expect EXACT double equality. for (int i = 0; i < keys.length; i++) { assertEquals("Wrong value at index " + i, values[i], sda.get(keys[i]), EXACT_PRECISION); } // Now check something that was never put in assertEquals("Wrong value for absent index", 0, sda.get(100000), EXACT_PRECISION); } @Test public void testAdd() { final SparseDoubleArray sda = new SparseDoubleArray(); sda.put(4, 6.1); sda.add(4, -1.2); sda.add(2, -1.2); assertEquals(6.1 - 1.2, sda.get(4), PRECISION); assertEquals(-1.2, sda.get(2), PRECISION); } @Test public void testKeyValueAt() { final SparseDoubleArray sda = new SparseDoubleArray(); final int[] keys = {1, 6, -14, 53251, 5, -13412, 12, 0, 2}; final double[] values = {7, -12.4, 7, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, 4311236312.0 / 3431470161413514334123.0, 431123636434132151313412.0 / 34323.0, 0}; for (int i = 0; i < keys.length; i++) { sda.put(keys[i], values[i]); } // Sort the sample data. final ArrayMap<Integer, Double> map = new ArrayMap<>(keys.length); for (int i = 0; i < keys.length; i++) { map.put(keys[i], values[i]); } final int[] sortedKeys = Arrays.copyOf(keys, keys.length); Arrays.sort(sortedKeys); for (int i = 0; i < sortedKeys.length; i++) { final int expectedKey = sortedKeys[i]; final double expectedValue = map.get(expectedKey); assertEquals("Wrong key at index " + i, expectedKey, sda.keyAt(i), PRECISION); assertEquals("Wrong value at index " + i, expectedValue, sda.valueAt(i), PRECISION); } } } Loading
core/java/android/util/SparseDoubleArray.java 0 → 100644 +166 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util; /** * SparseDoubleArrays map integers to doubles. Unlike a normal array of doubles, * there can be gaps in the indices. It is intended to be more memory efficient * than using a HashMap to map Integers to Doubles, both because it avoids * auto-boxing keys and values and its data structure doesn't rely on an extra entry object * for each mapping. * * <p>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%.</p> * * <p>It is possible to iterate over the items in this container using * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> 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 <code>valueAt(int)</code>.</p> * * @see SparseLongArray * * @hide */ public class SparseDoubleArray implements Cloneable { /** * The int->double map, but storing the doubles as longs using * {@link Double#doubleToRawLongBits(double)}. */ private SparseLongArray mValues; /** Creates a new SparseDoubleArray containing no mappings. */ public SparseDoubleArray() { this(10); } /** * Creates a new SparseDoubleArray, containing no mappings, that will not * require any additional memory allocation to store the specified * 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 SparseDoubleArray(int initialCapacity) { mValues = new SparseLongArray(initialCapacity); } @Override public SparseDoubleArray clone() { SparseDoubleArray clone = null; try { clone = (SparseDoubleArray) super.clone(); clone.mValues = mValues.clone(); } catch (CloneNotSupportedException cnse) { /* ignore */ } return clone; } /** * Gets the double mapped from the specified key, or <code>0</code> * if no such mapping has been made. */ public double get(int key) { final int index = mValues.indexOfKey(key); if (index < 0) { return 0.0d; } return valueAt(index); } /** * Adds a mapping from the specified key to the specified value, * replacing the previous mapping from the specified key if there * was one. */ public void put(int key, double value) { mValues.put(key, Double.doubleToRawLongBits(value)); } /** * Adds a mapping from the specified key to the specified value, * <b>adding</b> its value to the previous mapping from the specified key if there * was one. * * <p>This differs from {@link #put} because instead of replacing any previous value, it adds * (in the numerical sense) to it. */ public void add(int key, double summand) { final double oldValue = get(key); put(key, oldValue + summand); } /** Returns the number of key-value mappings that this SparseDoubleArray currently stores. */ public int size() { return mValues.size(); } /** * Given an index in the range <code>0...size()-1</code>, returns * the key from the <code>index</code>th key-value mapping that this * SparseDoubleArray stores. * * @see SparseLongArray#keyAt(int) */ public int keyAt(int index) { return mValues.keyAt(index); } /** * Given an index in the range <code>0...size()-1</code>, returns * the value from the <code>index</code>th key-value mapping that this * SparseDoubleArray stores. * * @see SparseLongArray#valueAt(int) */ public double valueAt(int index) { return Double.longBitsToDouble(mValues.valueAt(index)); } /** * {@inheritDoc} * * <p>This implementation composes a string by iterating over its mappings. */ @Override public String toString() { if (size() <= 0) { return "{}"; } StringBuilder buffer = new StringBuilder(size() * 34); buffer.append('{'); for (int i = 0; i < size(); i++) { if (i > 0) { buffer.append(", "); } int key = keyAt(i); buffer.append(key); buffer.append('='); double value = valueAt(i); buffer.append(value); } buffer.append('}'); return buffer.toString(); } }
core/java/com/android/internal/os/BatteryStatsImpl.java +1 −75 Original line number Diff line number Diff line Loading @@ -89,6 +89,7 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; import android.util.SparseArray; import android.util.SparseDoubleArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeUtils; Loading Loading @@ -12547,81 +12548,6 @@ public class BatteryStatsImpl extends BatteryStats { } } /** * SparseDoubleArray map integers to doubles. * Its implementation is the same as that of {@link SparseLongArray}; see there for details. * * @see SparseLongArray */ private static class SparseDoubleArray { /** * The int->double map, but storing the doubles as longs using * {@link Double.doubleToRawLongBits(double)}. */ private final SparseLongArray mValues = new SparseLongArray(); /** * Gets the double mapped from the specified key, or <code>0</code> * if no such mapping has been made. */ public double get(int key) { if (mValues.indexOfKey(key) >= 0) { return Double.longBitsToDouble(mValues.get(key)); } return 0; } /** * Adds a mapping from the specified key to the specified value, * replacing the previous mapping from the specified key if there * was one. */ public void put(int key, double value) { mValues.put(key, Double.doubleToRawLongBits(value)); } /** * Adds a mapping from the specified key to the specified value, * <b>adding</b> to the previous mapping from the specified key if there * was one. */ public void add(int key, double summand) { final double oldValue = get(key); put(key, oldValue + summand); } /** * Returns the number of key-value mappings that this SparseDoubleArray * currently stores. */ public int size() { return mValues.size(); } /** * Given an index in the range <code>0...size()-1</code>, returns * the key from the <code>index</code>th key-value mapping that this * SparseDoubleArray stores. * * @see SparseLongArray#keyAt(int) */ public int keyAt(int index) { return mValues.keyAt(index); } /** * Given an index in the range <code>0...size()-1</code>, returns * the value from the <code>index</code>th key-value mapping that this * SparseDoubleArray stores. * * @see SparseLongArray#valueAt(int) */ public double valueAt(int index) { return Double.longBitsToDouble(mValues.valueAt(index)); } } /** * Read and record Rail Energy data. */ Loading
core/tests/coretests/src/android/util/SparseDoubleArrayTest.java 0 → 100644 +108 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util; import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; /** * Internal tests for {@link SparseDoubleArray}. * * Run using: * atest FrameworksCoreTests:android.util.SparseDoubleArrayTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class SparseDoubleArrayTest { private static final double EXACT_PRECISION = 0; private static final double PRECISION = 0.000000001; @Test public void testPutGet() { final SparseDoubleArray sda = new SparseDoubleArray(); assertEquals("Array should be empty", 0, sda.size()); final int[] keys = {1, 6, -14, 53251, 5, -13412, 12, 0, 2}; final double[] values = {7, -12.4, 7, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, 4311236312.0 / 3431470161413514334123.0, 431123636434132151313412.0 / 34323.0, 0}; for (int i = 0; i < keys.length; i++) { sda.put(keys[i], values[i]); } assertEquals("Wrong size array", keys.length, sda.size()); // Due to the implementation, we actually expect EXACT double equality. for (int i = 0; i < keys.length; i++) { assertEquals("Wrong value at index " + i, values[i], sda.get(keys[i]), EXACT_PRECISION); } // Now check something that was never put in assertEquals("Wrong value for absent index", 0, sda.get(100000), EXACT_PRECISION); } @Test public void testAdd() { final SparseDoubleArray sda = new SparseDoubleArray(); sda.put(4, 6.1); sda.add(4, -1.2); sda.add(2, -1.2); assertEquals(6.1 - 1.2, sda.get(4), PRECISION); assertEquals(-1.2, sda.get(2), PRECISION); } @Test public void testKeyValueAt() { final SparseDoubleArray sda = new SparseDoubleArray(); final int[] keys = {1, 6, -14, 53251, 5, -13412, 12, 0, 2}; final double[] values = {7, -12.4, 7, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, 4311236312.0 / 3431470161413514334123.0, 431123636434132151313412.0 / 34323.0, 0}; for (int i = 0; i < keys.length; i++) { sda.put(keys[i], values[i]); } // Sort the sample data. final ArrayMap<Integer, Double> map = new ArrayMap<>(keys.length); for (int i = 0; i < keys.length; i++) { map.put(keys[i], values[i]); } final int[] sortedKeys = Arrays.copyOf(keys, keys.length); Arrays.sort(sortedKeys); for (int i = 0; i < sortedKeys.length; i++) { final int expectedKey = sortedKeys[i]; final double expectedValue = map.get(expectedKey); assertEquals("Wrong key at index " + i, expectedKey, sda.keyAt(i), PRECISION); assertEquals("Wrong value at index " + i, expectedValue, sda.valueAt(i), PRECISION); } } }