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 17package com.android.internal.util; 18 19import static com.android.internal.util.ArrayUtils.isEmpty; 20 21import android.annotation.NonNull; 22import android.annotation.Nullable; 23import android.util.ArraySet; 24import android.util.ExceptionUtils; 25 26import com.android.internal.util.FunctionalUtils.ThrowingConsumer; 27 28import java.util.ArrayList; 29import java.util.Collection; 30import java.util.Collections; 31import java.util.List; 32import java.util.Set; 33import java.util.function.*; 34import 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 */ 42public class 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 */ 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 */ 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 /** 90 * Returns a list of items resulting from applying the given function to each element of the 91 * provided list. 92 * 93 * The resulting list will have the same {@link #size} as the input one. 94 * 95 * This is similar to {@link Stream#map} but without the overhead of creating an intermediate 96 * {@link Stream} instance 97 */ 98 public static @NonNull <I, O> List<O> map(@Nullable List<I> cur, 99 Function<? super I, ? extends O> f) { 100 if (isEmpty(cur)) return Collections.emptyList(); 101 final ArrayList<O> result = new ArrayList<>(); 102 for (int i = 0; i < cur.size(); i++) { 103 result.add(f.apply(cur.get(i))); 104 } 105 return result; 106 } 107 108 /** 109 * @see #map(List, Function) 110 */ 111 public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur, 112 Function<? super I, ? extends O> f) { 113 if (isEmpty(cur)) return Collections.emptySet(); 114 ArraySet<O> result = new ArraySet<>(); 115 if (cur instanceof ArraySet) { 116 ArraySet<I> arraySet = (ArraySet<I>) cur; 117 int size = arraySet.size(); 118 for (int i = 0; i < size; i++) { 119 result.add(f.apply(arraySet.valueAt(i))); 120 } 121 } else { 122 for (I item : cur) { 123 result.add(f.apply(item)); 124 } 125 } 126 return result; 127 } 128 129 /** 130 * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)} 131 * 132 * Calling this is equivalent (but more memory efficient) to: 133 * 134 * {@code 135 * filter( 136 * map(cur, f), 137 * i -> { i != null }) 138 * } 139 */ 140 public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur, 141 Function<? super I, ? extends O> f) { 142 if (isEmpty(cur)) return Collections.emptyList(); 143 final ArrayList<O> result = new ArrayList<>(); 144 for (int i = 0; i < cur.size(); i++) { 145 O transformed = f.apply(cur.get(i)); 146 if (transformed != null) { 147 result.add(transformed); 148 } 149 } 150 return result; 151 } 152 153 /** 154 * Returns the given list, or an immutable empty list if the provided list is null 155 * 156 * This can be used to guarantee null-safety without paying the price of extra allocations 157 * 158 * @see Collections#emptyList 159 */ 160 public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) { 161 return cur == null ? Collections.emptyList() : cur; 162 } 163 164 /** 165 * Returns the given set, or an immutable empty set if the provided set is null 166 * 167 * This can be used to guarantee null-safety without paying the price of extra allocations 168 * 169 * @see Collections#emptySet 170 */ 171 public static @NonNull <T> Set<T> emptyIfNull(@Nullable Set<T> cur) { 172 return cur == null ? Collections.emptySet() : cur; 173 } 174 175 /** 176 * Returns the size of the given list, or 0 if the list is null 177 */ 178 public static int size(@Nullable Collection<?> cur) { 179 return cur != null ? cur.size() : 0; 180 } 181 182 /** 183 * Returns the elements of the given list that are of type {@code c} 184 */ 185 public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) { 186 if (isEmpty(list)) return Collections.emptyList(); 187 ArrayList<T> result = null; 188 for (int i = 0; i < list.size(); i++) { 189 final Object item = list.get(i); 190 if (c.isInstance(item)) { 191 result = ArrayUtils.add(result, (T) item); 192 } 193 } 194 return emptyIfNull(result); 195 } 196 197 /** 198 * Returns whether there exists at least one element in the list for which 199 * condition {@code predicate} is true 200 */ 201 public static <T> boolean any(@Nullable List<T> items, 202 java.util.function.Predicate<T> predicate) { 203 return find(items, predicate) != null; 204 } 205 206 /** 207 * Returns the first element from the list for which 208 * condition {@code predicate} is true, or null if there is no such element 209 */ 210 public static @Nullable <T> T find(@Nullable List<T> items, 211 java.util.function.Predicate<T> predicate) { 212 if (isEmpty(items)) return null; 213 for (int i = 0; i < items.size(); i++) { 214 final T item = items.get(i); 215 if (predicate.test(item)) return item; 216 } 217 return null; 218 } 219 220 /** 221 * Similar to {@link List#add}, but with support for list values of {@code null} and 222 * {@link Collections#emptyList} 223 */ 224 public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) { 225 if (cur == null || cur == Collections.emptyList()) { 226 cur = new ArrayList<>(); 227 } 228 cur.add(val); 229 return cur; 230 } 231 232 /** 233 * @see #add(List, Object) 234 */ 235 public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) { 236 if (cur == null || cur == Collections.emptySet()) { 237 cur = new ArraySet<>(); 238 } 239 cur.add(val); 240 return cur; 241 } 242 243 /** 244 * Similar to {@link List#remove}, but with support for list values of {@code null} and 245 * {@link Collections#emptyList} 246 */ 247 public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) { 248 if (isEmpty(cur)) { 249 return emptyIfNull(cur); 250 } 251 cur.remove(val); 252 return cur; 253 } 254 255 /** 256 * @see #remove(List, Object) 257 */ 258 public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) { 259 if (isEmpty(cur)) { 260 return emptyIfNull(cur); 261 } 262 cur.remove(val); 263 return cur; 264 } 265 266 /** 267 * @return a list that will not be affected by mutations to the given original list. 268 */ 269 public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) { 270 return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur); 271 } 272 273 /** 274 * @return a list that will not be affected by mutations to the given original list. 275 */ 276 public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) { 277 return isEmpty(cur) ? Collections.emptySet() : new ArraySet<>(cur); 278 } 279 280 /** 281 * Applies {@code action} to each element in {@code cur} 282 * 283 * This avoids creating an iterator if the given set is an {@link ArraySet} 284 */ 285 public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) { 286 if (cur == null || action == null) return; 287 int size = cur.size(); 288 if (size == 0) return; 289 try { 290 if (cur instanceof ArraySet) { 291 ArraySet<T> arraySet = (ArraySet<T>) cur; 292 for (int i = 0; i < size; i++) { 293 action.accept(arraySet.valueAt(i)); 294 } 295 } else { 296 for (T t : cur) { 297 action.accept(t); 298 } 299 } 300 } catch (Exception e) { 301 throw ExceptionUtils.propagate(e); 302 } 303 } 304} 305