Merge "SparseDoubleArray" into sc-dev

This commit is contained in:
Adam Bookatz
2021-03-22 21:45:29 +00:00
committed by Android (Google) Code Review
3 changed files with 275 additions and 75 deletions

View File

@@ -0,0 +1,166 @@
/*
* 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();
}
}

View File

@@ -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;
@@ -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.
*/

View File

@@ -0,0 +1,108 @@
/*
* 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);
}
}
}