/* * 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 Any class * @param Any class * @hide */ @TestApi @android.ravenwood.annotation.RavenwoodKeepWholeClass public class SparseArrayMap { private final SparseArray> 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 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 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 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 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 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 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 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 consumer) { for (int i = numMaps() - 1; i >= 0; --i) { ArrayMap data = mData.valueAt(i); for (int j = data.size() - 1; j >= 0; --j) { consumer.accept(data.valueAt(j)); } } } /** * @param Any class * @param Any class * @hide */ public interface TriConsumer { /** 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 consumer) { for (int iIdx = numMaps() - 1; iIdx >= 0; --iIdx) { final int i = mData.keyAt(iIdx); final ArrayMap data = mData.valueAt(iIdx); for (int kIdx = data.size() - 1; kIdx >= 0; --kIdx) { consumer.accept(i, data.keyAt(kIdx), data.valueAt(kIdx)); } } } }