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