| /* |
| * 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)); |
| } |
| } |
| } |
| } |