• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.utils;
17 
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.LinkedHashMap;
22 import java.util.LinkedList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.function.Function;
26 import java.util.function.Predicate;
27 import java.util.function.Supplier;
28 import java.util.stream.Collector;
29 import java.util.stream.Collectors;
30 import software.amazon.awssdk.annotations.SdkProtectedApi;
31 
32 @SdkProtectedApi
33 public final class CollectionUtils {
34 
CollectionUtils()35     private CollectionUtils() {
36     }
37 
isNullOrEmpty(Collection<?> collection)38     public static boolean isNullOrEmpty(Collection<?> collection) {
39         return collection == null || collection.isEmpty();
40     }
41 
isNullOrEmpty(Map<?, ?> map)42     public static boolean isNullOrEmpty(Map<?, ?> map) {
43         return map == null || map.isEmpty();
44     }
45 
isNotEmpty(Map<?, ?> map)46     public static boolean isNotEmpty(Map<?, ?> map) {
47         return map != null && !map.isEmpty();
48     }
49 
50     /**
51      * Returns a new list containing the second list appended to the first list.
52      */
mergeLists(List<T> list1, List<T> list2)53     public static <T> List<T> mergeLists(List<T> list1, List<T> list2) {
54         List<T> merged = new LinkedList<>();
55         if (list1 != null) {
56             merged.addAll(list1);
57         }
58         if (list2 != null) {
59             merged.addAll(list2);
60         }
61         return merged;
62     }
63 
64     /**
65      * @param list List to get first element from.
66      * @param <T> Type of elements in the list.
67      * @return The first element in the list if it exists. If the list is null or empty this will
68      * return null.
69      */
firstIfPresent(List<T> list)70     public static <T> T firstIfPresent(List<T> list) {
71         if (list == null || list.isEmpty()) {
72             return null;
73         } else {
74             return list.get(0);
75         }
76     }
77 
78     /**
79      * Perform a deep copy of the provided map of lists. This only performs a deep copy of the map and lists. Entries are not
80      * copied, so care should be taken to ensure that entries are immutable if preventing unwanted mutations of the elements is
81      * desired.
82      */
deepCopyMap(Map<T, ? extends List<U>> map)83     public static <T, U> Map<T, List<U>> deepCopyMap(Map<T, ? extends List<U>> map) {
84         return deepCopyMap(map, () -> new LinkedHashMap<>(map.size()));
85     }
86 
87     /**
88      * Perform a deep copy of the provided map of lists. This only performs a deep copy of the map and lists. Entries are not
89      * copied, so care should be taken to ensure that entries are immutable if preventing unwanted mutations of the elements is
90      * desired.
91      */
deepCopyMap(Map<T, ? extends List<U>> map, Supplier<Map<T, List<U>>> mapConstructor)92     public static <T, U> Map<T, List<U>> deepCopyMap(Map<T, ? extends List<U>> map, Supplier<Map<T, List<U>>> mapConstructor) {
93         Map<T, List<U>> result = mapConstructor.get();
94         map.forEach((k, v) -> result.put(k, new ArrayList<>(v)));
95         return result;
96     }
97 
unmodifiableMapOfLists(Map<T, List<U>> map)98     public static <T, U> Map<T, List<U>> unmodifiableMapOfLists(Map<T, List<U>> map) {
99         return new UnmodifiableMapOfLists<>(map);
100     }
101 
102     /**
103      * Perform a deep copy of the provided map of lists, and make the result unmodifiable.
104      *
105      * This is equivalent to calling {@link #deepCopyMap} followed by {@link #unmodifiableMapOfLists}.
106      */
deepUnmodifiableMap(Map<T, ? extends List<U>> map)107     public static <T, U> Map<T, List<U>> deepUnmodifiableMap(Map<T, ? extends List<U>> map) {
108         return unmodifiableMapOfLists(deepCopyMap(map));
109     }
110 
111     /**
112      * Perform a deep copy of the provided map of lists, and make the result unmodifiable.
113      *
114      * This is equivalent to calling {@link #deepCopyMap} followed by {@link #unmodifiableMapOfLists}.
115      */
deepUnmodifiableMap(Map<T, ? extends List<U>> map, Supplier<Map<T, List<U>>> mapConstructor)116     public static <T, U> Map<T, List<U>> deepUnmodifiableMap(Map<T, ? extends List<U>> map,
117                                                              Supplier<Map<T, List<U>>> mapConstructor) {
118         return unmodifiableMapOfLists(deepCopyMap(map, mapConstructor));
119     }
120 
121 
122     /**
123      * Collect a stream of {@link Map.Entry} to a {@link Map} with the same key/value types
124      * @param <K> the key type
125      * @param <V> the value type
126      * @return a map
127      */
toMap()128     public static <K, V> Collector<Map.Entry<K, V>, ?, Map<K, V>> toMap() {
129         return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue);
130     }
131 
132     /**
133      * Transforms the values of a map to another map with the same keys, using the supplied function.
134      *
135      * @param inputMap the input map
136      * @param mapper the function used to transform the map values
137      * @param <K> the key type
138      * @param <VInT> the value type for the input map
139      * @param <VOutT> the value type for the output map
140      * @return a map
141      */
mapValues(Map<K, VInT> inputMap, Function<VInT, VOutT> mapper)142     public static <K, VInT, VOutT> Map<K, VOutT> mapValues(Map<K, VInT> inputMap, Function<VInT, VOutT> mapper) {
143         return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> mapper.apply(e.getValue())));
144     }
145 
146     /**
147      * Filters a map based on a condition
148      *
149      * @param map the input map
150      * @param condition the predicate to filter on
151      * @param <K> the key type
152      * @param <V> the value type
153      * @return the filtered map
154      */
filterMap(Map<K, V> map, Predicate<Map.Entry<K, V>> condition)155     public static <K, V> Map<K, V> filterMap(Map<K, V> map, Predicate<Map.Entry<K, V>> condition) {
156         return map.entrySet()
157                   .stream()
158                   .filter(condition)
159                   .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
160     }
161 
162     /**
163      * Return a new map that is the inverse of the supplied map, with the values becoming the keys
164      * and vice versa. Requires the values to be unique.
165      *
166      * @param inputMap a map where both the keys and values are unique
167      * @param <K> the key type
168      * @param <V> the value type
169      * @return a map
170      */
inverseMap(Map<V, K> inputMap)171     public static <K, V> Map<K, V> inverseMap(Map<V, K> inputMap) {
172         return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
173     }
174 
175     /**
176      * For a collection of values of type {@code V} that can all be converted to type {@code K}, create a map that
177      * indexes all of the values by {@code K}. This requires that no two values map to the same index.
178      *
179      * @param values the collection of values to index
180      * @param indexFunction the function used to convert a value to its index
181      * @param <K> the index (or key) type
182      * @param <V> the value type
183      * @return a (modifiable) map that indexes K to its unique value V
184      * @throws IllegalArgumentException if any of the values map to the same index
185      */
uniqueIndex(Iterable<V> values, Function<? super V, K> indexFunction)186     public static <K, V> Map<K, V> uniqueIndex(Iterable<V> values, Function<? super V, K> indexFunction) {
187         Map<K, V> map = new HashMap<>();
188         for (V value : values) {
189             K index = indexFunction.apply(value);
190             V prev = map.put(index, value);
191             Validate.isNull(prev, "No duplicate indices allowed but both %s and %s have the same index: %s",
192                             prev, value, index);
193         }
194         return map;
195     }
196 }
197