1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.util; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.TestApi; 22 23 import java.util.function.Consumer; 24 25 /** 26 * A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object 27 * associations. 28 * 29 * @param <K> Any class 30 * @param <V> Any class 31 * @hide 32 */ 33 @TestApi 34 public class SparseArrayMap<K, V> { 35 private final SparseArray<ArrayMap<K, V>> mData = new SparseArray<>(); 36 37 /** 38 * Add an entry associating obj with the int-K pair. 39 * 40 * @return the previous value associated with key, or null if there was no mapping for key. 41 * (A null return can also indicate that the map previously associated null with key, if the 42 * implementation supports null values.) 43 */ add(int key, @NonNull K mapKey, @Nullable V obj)44 public V add(int key, @NonNull K mapKey, @Nullable V obj) { 45 ArrayMap<K, V> data = mData.get(key); 46 if (data == null) { 47 data = new ArrayMap<>(); 48 mData.put(key, data); 49 } 50 return data.put(mapKey, obj); 51 } 52 53 /** Remove all entries from the map. */ clear()54 public void clear() { 55 for (int i = 0; i < mData.size(); ++i) { 56 mData.valueAt(i).clear(); 57 } 58 } 59 60 /** Return true if the structure contains an explicit entry for the int-K pair. */ contains(int key, @NonNull K mapKey)61 public boolean contains(int key, @NonNull K mapKey) { 62 return mData.contains(key) && mData.get(key).containsKey(mapKey); 63 } 64 65 /** Removes all the data for the key, if there was any. */ delete(int key)66 public void delete(int key) { 67 mData.delete(key); 68 } 69 70 /** 71 * Removes all the data for the keyIndex, if there was any. 72 * @hide 73 */ deleteAt(int keyIndex)74 public void deleteAt(int keyIndex) { 75 mData.removeAt(keyIndex); 76 } 77 78 /** 79 * Removes the data for the key and mapKey, if there was any. 80 * 81 * @return Returns the value that was stored under the keys, or null if there was none. 82 */ 83 @Nullable delete(int key, @NonNull K mapKey)84 public V delete(int key, @NonNull K mapKey) { 85 ArrayMap<K, V> data = mData.get(key); 86 if (data != null) { 87 return data.remove(mapKey); 88 } 89 return null; 90 } 91 92 /** 93 * Removes the data for the keyIndex and mapIndex, if there was any. 94 * @hide 95 */ deleteAt(int keyIndex, int mapIndex)96 public void deleteAt(int keyIndex, int mapIndex) { 97 mData.valueAt(keyIndex).removeAt(mapIndex); 98 } 99 100 /** 101 * Get the value associated with the int-K pair. 102 */ 103 @Nullable get(int key, @NonNull K mapKey)104 public V get(int key, @NonNull K mapKey) { 105 ArrayMap<K, V> data = mData.get(key); 106 if (data != null) { 107 return data.get(mapKey); 108 } 109 return null; 110 } 111 112 /** 113 * Returns the value to which the specified key and mapKey are mapped, or defaultValue if this 114 * map contains no mapping for them. 115 */ 116 @Nullable getOrDefault(int key, @NonNull K mapKey, V defaultValue)117 public V getOrDefault(int key, @NonNull K mapKey, V defaultValue) { 118 if (mData.contains(key)) { 119 ArrayMap<K, V> data = mData.get(key); 120 if (data != null && data.containsKey(mapKey)) { 121 return data.get(mapKey); 122 } 123 } 124 return defaultValue; 125 } 126 127 /** @see SparseArray#indexOfKey */ indexOfKey(int key)128 public int indexOfKey(int key) { 129 return mData.indexOfKey(key); 130 } 131 132 /** 133 * Returns the index of the mapKey. 134 * 135 * @see SparseArray#indexOfKey 136 */ indexOfKey(int key, @NonNull K mapKey)137 public int indexOfKey(int key, @NonNull K mapKey) { 138 ArrayMap<K, V> data = mData.get(key); 139 if (data != null) { 140 return data.indexOfKey(mapKey); 141 } 142 return -1; 143 } 144 145 /** Returns the key at the given index. */ keyAt(int index)146 public int keyAt(int index) { 147 return mData.keyAt(index); 148 } 149 150 /** Returns the map's key at the given mapIndex for the given keyIndex. */ 151 @NonNull keyAt(int keyIndex, int mapIndex)152 public K keyAt(int keyIndex, int mapIndex) { 153 return mData.valueAt(keyIndex).keyAt(mapIndex); 154 } 155 156 /** Returns the size of the outer array. */ numMaps()157 public int numMaps() { 158 return mData.size(); 159 } 160 161 /** Returns the number of elements in the map of the given key. */ numElementsForKey(int key)162 public int numElementsForKey(int key) { 163 ArrayMap<K, V> data = mData.get(key); 164 return data == null ? 0 : data.size(); 165 } 166 167 /** 168 * Returns the number of elements in the map of the given keyIndex. 169 * @hide 170 */ numElementsForKeyAt(int keyIndex)171 public int numElementsForKeyAt(int keyIndex) { 172 ArrayMap<K, V> data = mData.valueAt(keyIndex); 173 return data == null ? 0 : data.size(); 174 } 175 176 /** Returns the value V at the given key and map index. */ 177 @Nullable valueAt(int keyIndex, int mapIndex)178 public V valueAt(int keyIndex, int mapIndex) { 179 return mData.valueAt(keyIndex).valueAt(mapIndex); 180 } 181 182 /** Iterate through all int-K pairs and operate on all of the values. */ forEach(@onNull Consumer<V> consumer)183 public void forEach(@NonNull Consumer<V> consumer) { 184 for (int i = numMaps() - 1; i >= 0; --i) { 185 ArrayMap<K, V> data = mData.valueAt(i); 186 for (int j = data.size() - 1; j >= 0; --j) { 187 consumer.accept(data.valueAt(j)); 188 } 189 } 190 } 191 192 /** 193 * @param <K> Any class 194 * @param <V> Any class 195 * @hide 196 */ 197 public interface TriConsumer<K, V> { 198 /** Consume the int-K-V tuple. */ accept(int key, K mapKey, V value)199 void accept(int key, K mapKey, V value); 200 } 201 202 /** 203 * Iterate through all int-K pairs and operate on all of the values. 204 * @hide 205 */ forEach(@onNull TriConsumer<K, V> consumer)206 public void forEach(@NonNull TriConsumer<K, V> consumer) { 207 for (int iIdx = numMaps() - 1; iIdx >= 0; --iIdx) { 208 final int i = mData.keyAt(iIdx); 209 final ArrayMap<K, V> data = mData.valueAt(iIdx); 210 for (int kIdx = data.size() - 1; kIdx >= 0; --kIdx) { 211 consumer.accept(i, data.keyAt(kIdx), data.valueAt(kIdx)); 212 } 213 } 214 } 215 } 216