CollectionUtils.java revision 0c4a9266264d37e724f7372ef7ef932cf60c505c
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;
23
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.Collections;
27import java.util.List;
28import java.util.Set;
29import java.util.function.Function;
30import 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 */
38public class 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
196    public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
197        return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
198    }
199}
200