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