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 /** Add an entry associating obj with the int-K pair. */ add(int key, @NonNull K mapKey, @Nullable V obj)38 public void add(int key, @NonNull K mapKey, @Nullable V obj) { 39 ArrayMap<K, V> data = mData.get(key); 40 if (data == null) { 41 data = new ArrayMap<>(); 42 mData.put(key, data); 43 } 44 data.put(mapKey, obj); 45 } 46 47 /** Remove all entries from the map. */ clear()48 public void clear() { 49 for (int i = 0; i < mData.size(); ++i) { 50 mData.valueAt(i).clear(); 51 } 52 } 53 54 /** Return true if the structure contains an explicit entry for the int-K pair. */ contains(int key, @NonNull K mapKey)55 public boolean contains(int key, @NonNull K mapKey) { 56 return mData.contains(key) && mData.get(key).containsKey(mapKey); 57 } 58 59 /** Removes all the data for the key, if there was any. */ delete(int key)60 public void delete(int key) { 61 mData.delete(key); 62 } 63 64 /** 65 * Removes all the data for the keyIndex, if there was any. 66 * @hide 67 */ deleteAt(int keyIndex)68 public void deleteAt(int keyIndex) { 69 mData.removeAt(keyIndex); 70 } 71 72 /** 73 * Removes the data for the key and mapKey, if there was any. 74 * 75 * @return Returns the value that was stored under the keys, or null if there was none. 76 */ 77 @Nullable delete(int key, @NonNull K mapKey)78 public V delete(int key, @NonNull K mapKey) { 79 ArrayMap<K, V> data = mData.get(key); 80 if (data != null) { 81 return data.remove(mapKey); 82 } 83 return null; 84 } 85 86 /** 87 * Get the value associated with the int-K pair. 88 */ 89 @Nullable get(int key, @NonNull K mapKey)90 public V get(int key, @NonNull K mapKey) { 91 ArrayMap<K, V> data = mData.get(key); 92 if (data != null) { 93 return data.get(mapKey); 94 } 95 return null; 96 } 97 98 /** 99 * Returns the value to which the specified key and mapKey are mapped, or defaultValue if this 100 * map contains no mapping for them. 101 */ 102 @Nullable getOrDefault(int key, @NonNull K mapKey, V defaultValue)103 public V getOrDefault(int key, @NonNull K mapKey, V defaultValue) { 104 if (mData.contains(key)) { 105 ArrayMap<K, V> data = mData.get(key); 106 if (data != null && data.containsKey(mapKey)) { 107 return data.get(mapKey); 108 } 109 } 110 return defaultValue; 111 } 112 113 /** @see SparseArray#indexOfKey */ indexOfKey(int key)114 public int indexOfKey(int key) { 115 return mData.indexOfKey(key); 116 } 117 118 /** 119 * Returns the index of the mapKey. 120 * 121 * @see SparseArray#indexOfKey 122 */ indexOfKey(int key, @NonNull K mapKey)123 public int indexOfKey(int key, @NonNull K mapKey) { 124 ArrayMap<K, V> data = mData.get(key); 125 if (data != null) { 126 return data.indexOfKey(mapKey); 127 } 128 return -1; 129 } 130 131 /** Returns the key at the given index. */ keyAt(int index)132 public int keyAt(int index) { 133 return mData.keyAt(index); 134 } 135 136 /** Returns the map's key at the given mapIndex for the given keyIndex. */ 137 @NonNull keyAt(int keyIndex, int mapIndex)138 public K keyAt(int keyIndex, int mapIndex) { 139 return mData.valueAt(keyIndex).keyAt(mapIndex); 140 } 141 142 /** Returns the size of the outer array. */ numMaps()143 public int numMaps() { 144 return mData.size(); 145 } 146 147 /** Returns the number of elements in the map of the given key. */ numElementsForKey(int key)148 public int numElementsForKey(int key) { 149 ArrayMap<K, V> data = mData.get(key); 150 return data == null ? 0 : data.size(); 151 } 152 153 /** 154 * Returns the number of elements in the map of the given keyIndex. 155 * @hide 156 */ numElementsForKeyAt(int keyIndex)157 public int numElementsForKeyAt(int keyIndex) { 158 ArrayMap<K, V> data = mData.valueAt(keyIndex); 159 return data == null ? 0 : data.size(); 160 } 161 162 /** Returns the value V at the given key and map index. */ 163 @Nullable valueAt(int keyIndex, int mapIndex)164 public V valueAt(int keyIndex, int mapIndex) { 165 return mData.valueAt(keyIndex).valueAt(mapIndex); 166 } 167 168 /** Iterate through all int-K pairs and operate on all of the values. */ forEach(@onNull Consumer<V> consumer)169 public void forEach(@NonNull Consumer<V> consumer) { 170 for (int i = numMaps() - 1; i >= 0; --i) { 171 ArrayMap<K, V> data = mData.valueAt(i); 172 for (int j = data.size() - 1; j >= 0; --j) { 173 consumer.accept(data.valueAt(j)); 174 } 175 } 176 } 177 178 /** 179 * @param <K> Any class 180 * @param <V> Any class 181 * @hide 182 */ 183 public interface TriConsumer<K, V> { 184 /** Consume the int-K-V tuple. */ accept(int key, K mapKey, V value)185 void accept(int key, K mapKey, V value); 186 } 187 188 /** 189 * Iterate through all int-K pairs and operate on all of the values. 190 * @hide 191 */ forEach(@onNull TriConsumer<K, V> consumer)192 public void forEach(@NonNull TriConsumer<K, V> consumer) { 193 for (int iIdx = numMaps() - 1; iIdx >= 0; --iIdx) { 194 final int i = mData.keyAt(iIdx); 195 final ArrayMap<K, V> data = mData.valueAt(iIdx); 196 for (int kIdx = data.size() - 1; kIdx >= 0; --kIdx) { 197 consumer.accept(i, data.keyAt(kIdx), data.valueAt(kIdx)); 198 } 199 } 200 } 201 } 202