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 static com.android.internal.util.ArrayUtils.isEmpty;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Set;
29 import java.util.function.Function;
30 import java.util.stream.Stream;
31 
32 /**
33  * Utility methods for dealing with (typically {@link Nullable}) {@link Collection}s
34  *
35  * Unless a method specifies otherwise, a null value for a collection is treated as an empty
36  * collection of that type.
37  */
38 public class CollectionUtils {
CollectionUtils()39     private CollectionUtils() { /* cannot be instantiated */ }
40 
41     /**
42      * Returns a list of items from the provided list that match the given condition.
43      *
44      * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate
45      * {@link Stream} instance
46      */
filter(@ullable List<T> list, java.util.function.Predicate<? super T> predicate)47     public static @NonNull <T> List<T> filter(@Nullable List<T> list,
48             java.util.function.Predicate<? super T> predicate) {
49         ArrayList<T> result = null;
50         for (int i = 0; i < size(list); i++) {
51             final T item = list.get(i);
52             if (predicate.test(item)) {
53                 result = ArrayUtils.add(result, item);
54             }
55         }
56         return emptyIfNull(result);
57     }
58 
59     /**
60      * Returns a list of items resulting from applying the given function to each element of the
61      * provided list.
62      *
63      * The resulting list will have the same {@link #size} as the input one.
64      *
65      * This is similar to {@link Stream#map} but without the overhead of creating an intermediate
66      * {@link Stream} instance
67      */
map(@ullable List<I> cur, Function<? super I, ? extends O> f)68     public static @NonNull <I, O> List<O> map(@Nullable List<I> cur,
69             Function<? super I, ? extends O> f) {
70         if (isEmpty(cur)) return Collections.emptyList();
71         final ArrayList<O> result = new ArrayList<>();
72         for (int i = 0; i < cur.size(); i++) {
73             result.add(f.apply(cur.get(i)));
74         }
75         return result;
76     }
77 
78     /**
79      * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)}
80      *
81      * Calling this is equivalent (but more memory efficient) to:
82      *
83      * {@code
84      *      filter(
85      *          map(cur, f),
86      *          i -> { i != null })
87      * }
88      */
mapNotNull(@ullable List<I> cur, Function<? super I, ? extends O> f)89     public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur,
90             Function<? super I, ? extends O> f) {
91         if (isEmpty(cur)) return Collections.emptyList();
92         final ArrayList<O> result = new ArrayList<>();
93         for (int i = 0; i < cur.size(); i++) {
94             O transformed = f.apply(cur.get(i));
95             if (transformed != null) {
96                 result.add(transformed);
97             }
98         }
99         return result;
100     }
101 
102     /**
103      * Returns the given list, or an immutable empty list if the provided list is null
104      *
105      * This can be used to guarantee null-safety without paying the price of extra allocations
106      *
107      * @see Collections#emptyList
108      */
emptyIfNull(@ullable List<T> cur)109     public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
110         return cur == null ? Collections.emptyList() : cur;
111     }
112 
113     /**
114      * Returns the given set, or an immutable empty set if the provided set is null
115      *
116      * This can be used to guarantee null-safety without paying the price of extra allocations
117      *
118      * @see Collections#emptySet
119      */
emptyIfNull(@ullable Set<T> cur)120     public static @NonNull <T> Set<T> emptyIfNull(@Nullable Set<T> cur) {
121         return cur == null ? Collections.emptySet() : cur;
122     }
123 
124     /**
125      * Returns the size of the given list, or 0 if the list is null
126      */
size(@ullable Collection<?> cur)127     public static int size(@Nullable Collection<?> cur) {
128         return cur != null ? cur.size() : 0;
129     }
130 
131     /**
132      * Returns the elements of the given list that are of type {@code c}
133      */
filter(@ullable List<?> list, Class<T> c)134     public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
135         if (isEmpty(list)) return Collections.emptyList();
136         ArrayList<T> result = null;
137         for (int i = 0; i < list.size(); i++) {
138             final Object item = list.get(i);
139             if (c.isInstance(item)) {
140                 result = ArrayUtils.add(result, (T) item);
141             }
142         }
143         return emptyIfNull(result);
144     }
145 
146     /**
147      * Returns whether there exists at least one element in the list for which
148      * condition {@code predicate} is true
149      */
any(@ullable List<T> items, java.util.function.Predicate<T> predicate)150     public static <T> boolean any(@Nullable List<T> items,
151             java.util.function.Predicate<T> predicate) {
152         return find(items, predicate) != null;
153     }
154 
155     /**
156      * Returns the first element from the list for which
157      * condition {@code predicate} is true, or null if there is no such element
158      */
find(@ullable List<T> items, java.util.function.Predicate<T> predicate)159     public static @Nullable <T> T find(@Nullable List<T> items,
160             java.util.function.Predicate<T> predicate) {
161         if (isEmpty(items)) return null;
162         for (int i = 0; i < items.size(); i++) {
163             final T item = items.get(i);
164             if (predicate.test(item)) return item;
165         }
166         return null;
167     }
168 
169     /**
170      * Similar to {@link List#add}, but with support for list values of {@code null} and
171      * {@link Collections#emptyList}
172      */
add(@ullable List<T> cur, T val)173     public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) {
174         if (cur == null || cur == Collections.emptyList()) {
175             cur = new ArrayList<>();
176         }
177         cur.add(val);
178         return cur;
179     }
180 
181     /**
182      * Similar to {@link List#remove}, but with support for list values of {@code null} and
183      * {@link Collections#emptyList}
184      */
remove(@ullable List<T> cur, T val)185     public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) {
186         if (isEmpty(cur)) {
187             return emptyIfNull(cur);
188         }
189         cur.remove(val);
190         return cur;
191     }
192 
193     /**
194      * @return a list that will not be affected by mutations to the given original list.
195      */
copyOf(@ullable List<T> cur)196     public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
197         return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
198     }
199 }
200