Merge "SparseDoubleArray" into sc-dev
This commit is contained in:
166
core/java/android/util/SparseDoubleArray.java
Normal file
166
core/java/android/util/SparseDoubleArray.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
108
core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
Normal file
108
core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user