blob: b4e1f59874b0984b641066956dc28347be79e4c3 [file] [log] [blame]
/*
* Copyright (C) 2019 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 android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import java.util.function.Consumer;
/**
* A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object
* associations.
*
* @param <K> Any class
* @param <V> Any class
* @hide
*/
@TestApi
public class SparseArrayMap<K, V> {
private final SparseArray<ArrayMap<K, V>> mData = new SparseArray<>();
/**
* Add an entry associating obj with the int-K pair.
*
* @return the previous value associated with key, or null if there was no mapping for key.
* (A null return can also indicate that the map previously associated null with key, if the
* implementation supports null values.)
*/
public V add(int key, @NonNull K mapKey, @Nullable V obj) {
ArrayMap<K, V> data = mData.get(key);
if (data == null) {
data = new ArrayMap<>();
mData.put(key, data);
}
return data.put(mapKey, obj);
}
/** Remove all entries from the map. */
public void clear() {
for (int i = 0; i < mData.size(); ++i) {
mData.valueAt(i).clear();
}
}
/** Return true if the structure contains an explicit entry for the int-K pair. */
public boolean contains(int key, @NonNull K mapKey) {
return mData.contains(key) && mData.get(key).containsKey(mapKey);
}
/** Removes all the data for the key, if there was any. */
public void delete(int key) {
mData.delete(key);
}
/**
* Removes all the data for the keyIndex, if there was any.
* @hide
*/
public void deleteAt(int keyIndex) {
mData.removeAt(keyIndex);
}
/**
* Removes the data for the key and mapKey, if there was any.
*
* @return Returns the value that was stored under the keys, or null if there was none.
*/
@Nullable
public V delete(int key, @NonNull K mapKey) {
ArrayMap<K, V> data = mData.get(key);
if (data != null) {
return data.remove(mapKey);
}
return null;
}
/**
* Removes the data for the keyIndex and mapIndex, if there was any.
* @hide
*/
public void deleteAt(int keyIndex, int mapIndex) {
mData.valueAt(keyIndex).removeAt(mapIndex);
}
/**
* Get the value associated with the int-K pair.
*/
@Nullable
public V get(int key, @NonNull K mapKey) {
ArrayMap<K, V> data = mData.get(key);
if (data != null) {
return data.get(mapKey);
}
return null;
}
/**
* Returns the value to which the specified key and mapKey are mapped, or defaultValue if this
* map contains no mapping for them.
*/
@Nullable
public V getOrDefault(int key, @NonNull K mapKey, V defaultValue) {
if (mData.contains(key)) {
ArrayMap<K, V> data = mData.get(key);
if (data != null && data.containsKey(mapKey)) {
return data.get(mapKey);
}
}
return defaultValue;
}
/** @see SparseArray#indexOfKey */
public int indexOfKey(int key) {
return mData.indexOfKey(key);
}
/**
* Returns the index of the mapKey.
*
* @see SparseArray#indexOfKey
*/
public int indexOfKey(int key, @NonNull K mapKey) {
ArrayMap<K, V> data = mData.get(key);
if (data != null) {
return data.indexOfKey(mapKey);
}
return -1;
}
/** Returns the key at the given index. */
public int keyAt(int index) {
return mData.keyAt(index);
}
/** Returns the map's key at the given mapIndex for the given keyIndex. */
@NonNull
public K keyAt(int keyIndex, int mapIndex) {
return mData.valueAt(keyIndex).keyAt(mapIndex);
}
/** Returns the size of the outer array. */
public int numMaps() {
return mData.size();
}
/** Returns the number of elements in the map of the given key. */
public int numElementsForKey(int key) {
ArrayMap<K, V> data = mData.get(key);
return data == null ? 0 : data.size();
}
/**
* Returns the number of elements in the map of the given keyIndex.
* @hide
*/
public int numElementsForKeyAt(int keyIndex) {
ArrayMap<K, V> data = mData.valueAt(keyIndex);
return data == null ? 0 : data.size();
}
/** Returns the value V at the given key and map index. */
@Nullable
public V valueAt(int keyIndex, int mapIndex) {
return mData.valueAt(keyIndex).valueAt(mapIndex);
}
/** Iterate through all int-K pairs and operate on all of the values. */
public void forEach(@NonNull Consumer<V> consumer) {
for (int i = numMaps() - 1; i >= 0; --i) {
ArrayMap<K, V> data = mData.valueAt(i);
for (int j = data.size() - 1; j >= 0; --j) {
consumer.accept(data.valueAt(j));
}
}
}
/**
* @param <K> Any class
* @param <V> Any class
* @hide
*/
public interface TriConsumer<K, V> {
/** Consume the int-K-V tuple. */
void accept(int key, K mapKey, V value);
}
/**
* Iterate through all int-K pairs and operate on all of the values.
* @hide
*/
public void forEach(@NonNull TriConsumer<K, V> consumer) {
for (int iIdx = numMaps() - 1; iIdx >= 0; --iIdx) {
final int i = mData.keyAt(iIdx);
final ArrayMap<K, V> data = mData.valueAt(iIdx);
for (int kIdx = data.size() - 1; kIdx >= 0; --kIdx) {
consumer.accept(i, data.keyAt(kIdx), data.valueAt(kIdx));
}
}
}
}