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