1 /* 2 * Copyright (C) 2017 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 com.android.internal.util; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.util.ArraySet; 22 import android.util.ExceptionUtils; 23 24 import com.android.internal.util.FunctionalUtils.ThrowingConsumer; 25 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.Collections; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.Set; 32 import java.util.function.Function; 33 import java.util.function.Predicate; 34 import java.util.stream.Stream; 35 36 /** 37 * Utility methods for dealing with (typically {@code Nullable}) {@link Collection}s 38 * 39 * Unless a method specifies otherwise, a null value for a collection is treated as an empty 40 * collection of that type. 41 */ 42 public class CollectionUtils { CollectionUtils()43 private CollectionUtils() { /* cannot be instantiated */ } 44 45 /** 46 * Returns a list of items from the provided list that match the given condition. 47 * 48 * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate 49 * {@link Stream} instance 50 */ filter(@ullable List<T> list, java.util.function.Predicate<? super T> predicate)51 public static @NonNull <T> List<T> filter(@Nullable List<T> list, 52 java.util.function.Predicate<? super T> predicate) { 53 ArrayList<T> result = null; 54 for (int i = 0; i < size(list); i++) { 55 final T item = list.get(i); 56 if (predicate.test(item)) { 57 result = ArrayUtils.add(result, item); 58 } 59 } 60 return emptyIfNull(result); 61 } 62 63 /** 64 * @see #filter(List, java.util.function.Predicate) 65 */ filter(@ullable Set<T> set, java.util.function.Predicate<? super T> predicate)66 public static @NonNull <T> Set<T> filter(@Nullable Set<T> set, 67 java.util.function.Predicate<? super T> predicate) { 68 if (set == null || set.size() == 0) return Collections.emptySet(); 69 ArraySet<T> result = null; 70 if (set instanceof ArraySet) { 71 ArraySet<T> arraySet = (ArraySet<T>) set; 72 int size = arraySet.size(); 73 for (int i = 0; i < size; i++) { 74 final T item = arraySet.valueAt(i); 75 if (predicate.test(item)) { 76 result = ArrayUtils.add(result, item); 77 } 78 } 79 } else { 80 for (T item : set) { 81 if (predicate.test(item)) { 82 result = ArrayUtils.add(result, item); 83 } 84 } 85 } 86 return emptyIfNull(result); 87 } 88 89 /** Add all elements matching {@code predicate} in {@code source} to {@code dest}. */ addIf(@ullable List<T> source, @NonNull Collection<? super T> dest, @Nullable Predicate<? super T> predicate)90 public static <T> void addIf(@Nullable List<T> source, @NonNull Collection<? super T> dest, 91 @Nullable Predicate<? super T> predicate) { 92 for (int i = 0; i < size(source); i++) { 93 final T item = source.get(i); 94 if (predicate.test(item)) { 95 dest.add(item); 96 } 97 } 98 } 99 100 /** 101 * Returns a list of items resulting from applying the given function to each element of the 102 * provided list. 103 * 104 * The resulting list will have the same {@link #size} as the input one. 105 * 106 * This is similar to {@link Stream#map} but without the overhead of creating an intermediate 107 * {@link Stream} instance 108 */ map(@ullable List<I> cur, Function<? super I, ? extends O> f)109 public static @NonNull <I, O> List<O> map(@Nullable List<I> cur, 110 Function<? super I, ? extends O> f) { 111 if (isEmpty(cur)) return Collections.emptyList(); 112 final ArrayList<O> result = new ArrayList<>(); 113 for (int i = 0; i < cur.size(); i++) { 114 result.add(f.apply(cur.get(i))); 115 } 116 return result; 117 } 118 119 /** 120 * @see #map(List, Function) 121 */ map(@ullable Set<I> cur, Function<? super I, ? extends O> f)122 public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur, 123 Function<? super I, ? extends O> f) { 124 if (isEmpty(cur)) return Collections.emptySet(); 125 ArraySet<O> result = new ArraySet<>(); 126 if (cur instanceof ArraySet) { 127 ArraySet<I> arraySet = (ArraySet<I>) cur; 128 int size = arraySet.size(); 129 for (int i = 0; i < size; i++) { 130 result.add(f.apply(arraySet.valueAt(i))); 131 } 132 } else { 133 for (I item : cur) { 134 result.add(f.apply(item)); 135 } 136 } 137 return result; 138 } 139 140 /** 141 * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)} 142 * 143 * Calling this is equivalent (but more memory efficient) to: 144 * 145 * {@code 146 * filter( 147 * map(cur, f), 148 * i -> { i != null }) 149 * } 150 */ mapNotNull(@ullable List<I> cur, Function<? super I, ? extends O> f)151 public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur, 152 Function<? super I, ? extends O> f) { 153 if (isEmpty(cur)) return Collections.emptyList(); 154 List<O> result = null; 155 for (int i = 0; i < cur.size(); i++) { 156 O transformed = f.apply(cur.get(i)); 157 if (transformed != null) { 158 result = add(result, transformed); 159 } 160 } 161 return emptyIfNull(result); 162 } 163 164 /** 165 * Returns the given list, or an immutable empty list if the provided list is null 166 * 167 * This can be used to guarantee null-safety without paying the price of extra allocations 168 * 169 * @see Collections#emptyList 170 */ emptyIfNull(@ullable List<T> cur)171 public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) { 172 return cur == null ? Collections.emptyList() : cur; 173 } 174 175 /** 176 * Returns the given set, or an immutable empty set if the provided set is null 177 * 178 * This can be used to guarantee null-safety without paying the price of extra allocations 179 * 180 * @see Collections#emptySet 181 */ emptyIfNull(@ullable Set<T> cur)182 public static @NonNull <T> Set<T> emptyIfNull(@Nullable Set<T> cur) { 183 return cur == null ? Collections.emptySet() : cur; 184 } 185 186 /** 187 * Returns the size of the given collection, or 0 if null 188 */ size(@ullable Collection<?> cur)189 public static int size(@Nullable Collection<?> cur) { 190 return cur != null ? cur.size() : 0; 191 } 192 193 /** 194 * Returns the size of the given map, or 0 if null 195 */ size(@ullable Map<?, ?> cur)196 public static int size(@Nullable Map<?, ?> cur) { 197 return cur != null ? cur.size() : 0; 198 } 199 200 /** 201 * Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null} 202 */ isEmpty(@ullable Collection<?> cur)203 public static boolean isEmpty(@Nullable Collection<?> cur) { 204 return size(cur) == 0; 205 } 206 207 /** 208 * Returns the elements of the given list that are of type {@code c} 209 */ filter(@ullable List<?> list, Class<T> c)210 public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) { 211 if (isEmpty(list)) return Collections.emptyList(); 212 ArrayList<T> result = null; 213 for (int i = 0; i < list.size(); i++) { 214 final Object item = list.get(i); 215 if (c.isInstance(item)) { 216 result = ArrayUtils.add(result, (T) item); 217 } 218 } 219 return emptyIfNull(result); 220 } 221 222 /** 223 * Returns whether there exists at least one element in the list for which 224 * condition {@code predicate} is true 225 */ any(@ullable List<T> items, java.util.function.Predicate<T> predicate)226 public static <T> boolean any(@Nullable List<T> items, 227 java.util.function.Predicate<T> predicate) { 228 return find(items, predicate) != null; 229 } 230 231 /** 232 * Returns the first element from the list for which 233 * condition {@code predicate} is true, or null if there is no such element 234 */ find(@ullable List<T> items, java.util.function.Predicate<T> predicate)235 public static @Nullable <T> T find(@Nullable List<T> items, 236 java.util.function.Predicate<T> predicate) { 237 if (isEmpty(items)) return null; 238 for (int i = 0; i < items.size(); i++) { 239 final T item = items.get(i); 240 if (predicate.test(item)) return item; 241 } 242 return null; 243 } 244 245 /** 246 * Similar to {@link List#add}, but with support for list values of {@code null} and 247 * {@link Collections#emptyList} 248 */ add(@ullable List<T> cur, T val)249 public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) { 250 if (cur == null || cur == Collections.emptyList()) { 251 cur = new ArrayList<>(); 252 } 253 cur.add(val); 254 return cur; 255 } 256 257 /** 258 * @see #add(List, Object) 259 */ add(@ullable Set<T> cur, T val)260 public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) { 261 if (cur == null || cur == Collections.emptySet()) { 262 cur = new ArraySet<>(); 263 } 264 cur.add(val); 265 return cur; 266 } 267 268 /** 269 * Similar to {@link List#remove}, but with support for list values of {@code null} and 270 * {@link Collections#emptyList} 271 */ remove(@ullable List<T> cur, T val)272 public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) { 273 if (isEmpty(cur)) { 274 return emptyIfNull(cur); 275 } 276 cur.remove(val); 277 return cur; 278 } 279 280 /** 281 * @see #remove(List, Object) 282 */ remove(@ullable Set<T> cur, T val)283 public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) { 284 if (isEmpty(cur)) { 285 return emptyIfNull(cur); 286 } 287 cur.remove(val); 288 return cur; 289 } 290 291 /** 292 * @return a list that will not be affected by mutations to the given original list. 293 */ copyOf(@ullable List<T> cur)294 public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) { 295 return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur); 296 } 297 298 /** 299 * @return a list that will not be affected by mutations to the given original list. 300 */ copyOf(@ullable Set<T> cur)301 public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) { 302 return isEmpty(cur) ? Collections.emptySet() : new ArraySet<>(cur); 303 } 304 305 /** 306 * Applies {@code action} to each element in {@code cur} 307 * 308 * This avoids creating an iterator if the given set is an {@link ArraySet} 309 */ forEach(@ullable Set<T> cur, @Nullable ThrowingConsumer<T> action)310 public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) { 311 if (cur == null || action == null) return; 312 int size = cur.size(); 313 if (size == 0) return; 314 try { 315 if (cur instanceof ArraySet) { 316 ArraySet<T> arraySet = (ArraySet<T>) cur; 317 for (int i = 0; i < size; i++) { 318 action.acceptOrThrow(arraySet.valueAt(i)); 319 } 320 } else { 321 for (T t : cur) { 322 action.acceptOrThrow(t); 323 } 324 } 325 } catch (Exception e) { 326 throw ExceptionUtils.propagate(e); 327 } 328 } 329 330 /** 331 * @return the first element if not empty/null, null otherwise 332 */ firstOrNull(@ullable List<T> cur)333 public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) { 334 return isEmpty(cur) ? null : cur.get(0); 335 } 336 337 /** 338 * @return the first element if not empty/null, null otherwise 339 */ firstOrNull(@ullable Collection<T> cur)340 public static @Nullable <T> T firstOrNull(@Nullable Collection<T> cur) { 341 return isEmpty(cur) ? null : cur.iterator().next(); 342 } 343 344 /** 345 * @return list of single given element if it's not null, empty list otherwise 346 */ singletonOrEmpty(@ullable T item)347 public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) { 348 return item == null ? Collections.emptyList() : Collections.singletonList(item); 349 } 350 } 351