org.opencastproject.util.data.Collections Maven / Gradle / Ivy
/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.util.data;
import static org.opencastproject.util.data.Option.option;
import static org.opencastproject.util.data.Option.some;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* This class provides functions to ease and secure the handling of collections by supporting a type safe -- at least to
* the extent Java's type system allows -- immutable and more functional style.
*
* Note that all functions do not mutate input collections unless otherwise stated.
*/
public final class Collections {
private Collections() {
}
// TODO check all clients of this method since it potentially breaks!
@SuppressWarnings("unchecked")
private static Collection buildFrom(Collection as) {
try {
return as.getClass().newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("Type " + as.getClass() + " needs a parameterless constructor");
}
}
/**
* Get a value from a map, creating and adding a new one, if the value is missing, i.e. it is null.
*
* @param c
* creates the missing value
*/
public static V getOrCreate(Map map, K key, Creator c) {
V v = map.get(key);
if (v == null) {
v = c.create();
map.put(key, v);
}
return v;
}
/**
* Get a value from a map, creating and adding a new one, if the value is missing, i.e. it is null.
*/
public static V getOrCreate(Map map, K key, Function0 f) {
V v = map.get(key);
if (v == null) {
v = f.apply();
map.put(key, v);
}
return v;
}
/**
* Apply a function f
to all elements of collection as
to produce a new collection
* bs
.
*
* An (empty) instance of the target collection has to be provided explicitly.
*
* @param as
* the source collection
* @param bs
* the (empty) target collection
* @param f
* the function to apply to each element of as
* @deprecated use {@link Monadics}
*/
@Deprecated
public static > M map(Collection as, M bs, Function f) {
for (A x : as) {
bs.add(f.apply(x));
}
return bs;
}
/**
* Apply a binary function (operator) to a start value and all elements of the list in turn.
*
* Example: (+) 0 [1, 2, 3] -> (((0 + 1) + 2) + 3)
*
* @deprecated use {@link Monadics}
*/
@Deprecated
public static B foldl(Collection as, B start, Function2 f) {
B fold = start;
for (A a : as) {
fold = f.apply(fold, a);
}
return fold;
}
/**
* Apply a function f
to all elements of collection as
to produce a new collection
* bs
.
*
* The type of collection as
needs a parameterless constructor.
*
* Please note that since java does not support higher-order polymorphism -- which is needed to capture the type of
* the collection -- some casting on the client side may still be necessary.
*
* @throws RuntimeException
* if the target collection cannot be created
* @deprecated use {@link Monadics}
*/
@Deprecated
public static Collection map(Collection as, Function f) {
Collection b = buildFrom(as);
for (A x : as) {
b.add(f.apply(x));
}
return b;
}
/**
* Apply a function f
to all elements of collection as
to produce a new collection
* bs
by concatenating the results.
*
* The type of collection as
needs a parameterless constructor.
*
* Please note that since java does not support higher-order polymorphism -- which is needed to capture the type of
* the collection -- some casting on the client side may still be necessary.
*
* @throws RuntimeException
* if the result collection cannot be created
* @deprecated use {@link Monadics}
*/
@Deprecated
public static Collection flatMap(Collection as, Function> f) {
Collection bs = buildFrom(as);
for (A a : as) {
bs.addAll(f.apply(a));
}
return bs;
}
/**
* Exactly like {@link #flatMap(java.util.Collection, Function)} but you have to provide the target collection
* yourself.
*
* @deprecated use {@link Monadics}
*/
@Deprecated
public static > M flatMap(Collection as, M bs, Function> f) {
for (A a : as) {
bs.addAll(f.apply(a));
}
return bs;
}
/**
* Returns the first element in as
that satisfies a predicate p
.
*
* @deprecated use {@link Monadics}
*/
@Deprecated
public static Option find(Collection as, Predicate p) {
for (A x : as) {
if (p.apply(x))
return some(x);
}
return Option.none();
}
/**
* Tests if at least one element in as
satisfies predicate p
.
*
* @deprecated use {@link Monadics}
*/
@Deprecated
public static boolean exists(Collection as, Predicate p) {
for (A a : as) {
if (p.apply(a))
return true;
}
return false;
}
/**
* Return a new collection containing only the elements that satisfy predicate p
.
*
* The type of collection as
needs a parameterless constructor.
*
* @deprecated use {@link Monadics}
*/
@Deprecated
public static > M filter(M as, Predicate p) {
@SuppressWarnings("unchecked")
final M filtered = (M) buildFrom(as);
for (A a : as) {
if (p.apply(a))
filtered.add(a);
}
return filtered;
}
/** Return the head of list as
or none
. */
public static Option head(List as) {
if (!as.isEmpty()) {
return some(as.get(0));
} else {
return Option.none();
}
}
/** Return the last element of the list. */
public static Option last(List as) {
return as.size() > 0 ? some(as.get(as.size() - 1)) : Option. none();
}
/** Return the last element of the array. */
public static Option last(A[] as) {
return as.length > 0 ? some(as[as.length - 1]) : Option. none();
}
/** Make a string from a collection separating each element by sep
. */
public static String mkString(Collection> as, String sep) {
final StringBuilder b = new StringBuilder();
for (Object a : as)
b.append(a).append(sep);
return b.substring(0, Math.max(b.length() - sep.length(), 0));
}
/** Append source collection as
to target
. */
public static , S extends Iterable extends A>> T appendTo(T target, S as) {
for (A a : as)
target.add(a);
return target;
}
/** Append source collections as
to target
. */
@SafeVarargs
public static , S extends Iterable extends A>> T appendToM(T target, S... as) {
for (S s : as) {
for (A a : s)
target.add(a);
}
return target;
}
/** Append source collections as
to target
. */
@SafeVarargs
public static , X extends A> T appendToA(T target, X... as) {
java.util.Collections.addAll(target, as);
return target;
}
/** Concatenates two iterables into a new list. */
public static > List concat(M as, M bs) {
List x = new ArrayList<>();
for (A a : as)
x.add(a);
for (A b : bs)
x.add(b);
return x;
}
/** Concatenates two lists. */
public static List concat(List extends A> as, List extends A> bs) {
List x = new ArrayList<>();
for (A a : as)
x.add(a);
for (A b : bs)
x.add(b);
return x;
}
/**
* Merge two maps where b
takes precedence.
*
* @return a new immutable map
*/
public static Map merge(Map extends A, ? extends B> a, Map extends A, ? extends B> b) {
final Map x = new HashMap<>();
x.putAll(a);
x.putAll(b);
return java.util.Collections.unmodifiableMap(x);
}
/**
* Merge two sets into one. b
takes precedence over a
.
*
* @return a new immutable set
*/
public static Set merge(Set extends A> a, Set extends A> b) {
final Set x = new HashSet<>();
x.addAll(a);
x.addAll(b);
return java.util.Collections.unmodifiableSet(x);
}
public static > List diff(M as, M bs) {
final List diff = toList(as.iterator());
for (A b : bs) {
diff.remove(b);
}
return diff;
}
/** Drain all elements of as
into a list. */
public static List toList(Iterator extends A> as) {
final List t = new ArrayList<>();
while (as.hasNext()) {
t.add(as.next());
}
return t;
}
/** Create a list of tuples (K, V) from a map. */
public static List> toList(Map map) {
List> list = new ArrayList<>();
for (Entry entry : map.entrySet()) {
list.add(Tuple.tuple(entry.getKey(), entry.getValue()));
}
return list;
}
@SafeVarargs
public static Map toList(Tuple extends K, ? extends V>... ts) {
final Map map = new HashMap<>(ts.length);
for (Tuple extends K, ? extends V> t : ts) {
map.put(t.getA(), t.getB());
}
return map;
}
/** Drain all elements of as
into a list. */
public static List toList(Collection as) {
return new ArrayList<>(as);
}
/** Return nil if a
is null or a list containing a
otherwise. */
@SuppressWarnings("unchecked")
public static List toList(A a) {
return a != null ? list(a) : Collections. nil();
}
/**
* Return the list as is or nil, if as
is null.
*
* @deprecated use {@link #nullToNil(java.util.List)}
*/
@Deprecated
public static List mkList(List as) {
return as != null ? as : Collections. nil();
}
/** Return the list as is or nil, if as
is null. */
public static List nullToNil(List as) {
return as != null ? as : Collections. nil();
}
/** Create a list from an array. */
@SafeVarargs
public static List list(A... as) {
final List t = new ArrayList<>();
java.util.Collections.addAll(t, as);
return t;
}
/** Create a list from an array. */
@SafeVarargs
public static List nonNullList(A... as) {
final List t = new ArrayList<>();
for (A a : as) {
if (null != a) {
t.add(a);
}
}
return t;
}
/** The empty list. */
@SuppressWarnings("unchecked")
public static List nil() {
return java.util.Collections.EMPTY_LIST;
}
/** The empty list. */
@SuppressWarnings("unchecked")
public static List nil(Class type) {
return java.util.Collections.EMPTY_LIST;
}
/** Construct a new list by prepending an element to a given list. */
public static List cons(A a, List extends A> as) {
final List target = new ArrayList<>(as.size() + 1);
target.add(a);
target.addAll(as);
return target;
}
/** Create a set from an array. */
@SafeVarargs
public static Set set(A... as) {
final Set t = new HashSet<>(as.length);
java.util.Collections.addAll(t, as);
return t;
}
/** Create a set from a list. */
public static Set toSet(List as) {
Set r = new HashSet<>(as.size());
for (A a : as)
r.add(a);
return r;
}
/** Create a map from a list of tuples (K, V). */
@SafeVarargs
public static Map map(Tuple extends K, ? extends V>... ts) {
final Map map = new HashMap<>(ts.length);
for (Tuple extends K, ? extends V> t : ts) {
map.put(t.getA(), t.getB());
}
return map;
}
/** Create a sorted map from a list of tuples (K, V) based on the natural ordering of K. */
@SafeVarargs
public static SortedMap smap(Tuple extends K, ? extends V>... ts) {
final SortedMap map = new TreeMap<>();
for (Tuple extends K, ? extends V> t : ts) {
map.put(t.getA(), t.getB());
}
return map;
}
/** Create a dictionary from a list of tuples (K, V). */
@SafeVarargs
public static Dictionary dict(Tuple extends K, ? extends V>... ts) {
final Dictionary dict = new Hashtable<>(ts.length);
for (Tuple extends K, ? extends V> t : ts) {
dict.put(t.getA(), t.getB());
}
return dict;
}
/** Create properties from a list of tuples (K, V). */
@SafeVarargs
public static Properties properties(Tuple... ts) {
Properties a = new Properties();
for (Tuple t : ts) {
a.setProperty(t.getA(), t.getB());
}
return a;
}
/** Convert a properties object into a typed immutable map. */
public static Map toMap(final Properties p) {
final Map m = new HashMap<>();
for (Map.Entry e : p.entrySet()) {
m.put(e.getKey().toString(), e.getValue().toString());
}
return java.util.Collections.unmodifiableMap(m);
}
/**
* Partition a list after some predicate group
into map
.
*
* Use e.g. ArrayListMultimap.create()
to create a multimap.
*
* @see #groupBy(Iterable, Function)
*/
public static Multimap groupBy(Multimap map,
Iterable extends V> values,
Function super V, ? extends K> group) {
for (V value : values) {
final K key = group.apply(value);
map.put(key, value);
}
return map;
}
/**
* Partition a list after some predicate group
into map
.
*
* @return an {@link ImmutableMultimap}
* @see #groupBy(com.google.common.collect.Multimap, Iterable, Function)
*/
public static ImmutableMultimap groupBy(Iterable extends V> values,
Function super V, ? extends K> group) {
final ImmutableMultimap.Builder map = ImmutableMultimap.builder();
for (V value : values) {
final K key = group.apply(value);
map.put(key, value);
}
return map.build();
}
/**
* Partition a list after some predicate group
into map
.
*/
public static Multimap makeMap(Multimap map,
Iterable extends X> values,
Function super X, Tuple> group) {
for (X value : values) {
final Tuple entry = group.apply(value);
map.put(entry.getA(), entry.getB());
}
return map;
}
/** Partition a list in chunks of size size
. The last chunk may be smaller. */
public static List> grouped(List as, int size) {
final List> grouped = new ArrayList<>((as.size() / size) + 1);
List group = new ArrayList<>(size);
grouped.add(group);
int count = size;
for (A a : as) {
if (count == 0) {
group = new ArrayList<>(size);
grouped.add(group);
count = size;
}
group.add(a);
count--;
}
return grouped;
}
/** Create a list of unique elements determined by a given criteria. */
public static Collection unique(List as, Function criteria) {
final Map unique = new HashMap<>();
for (A a : as) {
unique.put(criteria.apply(a), a);
}
return unique.values();
}
/**
* Partition a list after some predicate keyGen
. The partition function has to make sure that keys are
* unique per list element because each key holds only one value. Later values overwrite newer ones.
*
* The resulting map is an immutable {@link java.util.HashMap}.
*
* @see #asMap(java.util.Map, java.util.List, Function)
*/
public static Map asMap(List values, Function keyGen) {
return java.util.Collections.unmodifiableMap(asMap(new HashMap(), values, keyGen));
}
/**
* Partition a list after some predicate keyGen
into map
. The partition function has to make
* sure that keys are unique per list element because each key holds only one value. Later values overwrite newer
* ones.
*
* @see #asMap(java.util.List, Function)
*/
public static Map asMap(Map map, List values, Function keyGen) {
for (V value : values) {
final K key = keyGen.apply(value);
map.put(key, value);
}
return map;
}
/** Create an array from a collection. */
@SuppressWarnings("unchecked")
public static A[] toArray(Class elemType, Collection a) {
return a.toArray((A[]) Array.newInstance(elemType, a.size()));
}
/** Convert a collection of {@link Double}s into an array of primitive type. */
public static double[] toDoubleArray(Collection as) {
final double[] target = new double[as.size()];
int i = 0;
for (Double a : as) {
target[i] = a;
i++;
}
return target;
}
/** Convert a collection of {@link Float}s into an array of primitive type. */
public static float[] toFloatArray(Collection as) {
final float[] target = new float[as.size()];
int i = 0;
for (Float a : as) {
target[i] = a;
i++;
}
return target;
}
/** Convert a collection of {@link Integer}s into an array of primitive type. */
public static int[] toIntArray(Collection as) {
final int[] target = new int[as.size()];
int i = 0;
for (Integer a : as) {
target[i] = a;
i++;
}
return target;
}
/** Create an iterator form an array. */
@SafeVarargs
public static Iterator iterator(final A... as) {
return new Iterator() {
private int i = 0;
@Override
public boolean hasNext() {
return as.length > i;
}
@Override
public A next() {
if (i < as.length) {
return as[i++];
} else {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/** Create an iterator that repeats a
for the said times. */
public static Iterator repeat(final X a, final int times) {
return new Iterator() {
private int count = times;
@Override
public boolean hasNext() {
return count > 0;
}
@Override
public A next() {
count--;
return a;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/** Join two iterators. */
public static Iterator join(final Iterator a, final Iterator b) {
return new Iterator() {
@Override
public boolean hasNext() {
return a.hasNext() || b.hasNext();
}
@Override
public A next() {
return a.hasNext() ? a.next() : b.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Make an Iterator usable in a for comprehension like this:
*
*
* Iterator<A> as = ...
* for (A a : forc(as)) {
* ...
* }
*
*/
public static Iterable forc(final Iterator as) {
return new Iterable() {
@Override
public Iterator iterator() {
return as;
}
};
}
public static Function
© 2015 - 2025 Weber Informatics LLC | Privacy Policy