com.ajjpj.afoundation.collection.ACollectionHelper Maven / Gradle / Ivy
package com.ajjpj.afoundation.collection;
import com.ajjpj.afoundation.collection.immutable.ACollection;
import com.ajjpj.afoundation.collection.immutable.AOption;
import com.ajjpj.afoundation.collection.immutable.ASet;
import com.ajjpj.afoundation.collection.immutable.AbstractACollection;
import com.ajjpj.afoundation.function.AFunction1;
import com.ajjpj.afoundation.function.AFunction2;
import com.ajjpj.afoundation.function.APartialFunction;
import com.ajjpj.afoundation.function.APredicate;
import java.lang.reflect.Array;
import java.util.*;
/**
* This class consists of a number of useful methods that operate on a variety of collection types.
*
* @author arno
*/
public class ACollectionHelper {
/**
* Create a {@link java.util.Set} from an {@link Iterator}.
*/
public static Set createSet (Iterator elements) {
final Set result = new HashSet<> ();
while (elements.hasNext ()) result.add (elements.next ());
return result;
}
/**
* Create a {@link java.util.Set} from an {@link java.lang.Iterable}.
*/
public static Set createSet (Iterable elements) {
return createSet (elements.iterator ());
}
/**
* Create a {@link java.util.Set} from an {@link Iterator}, transforming each element.
*/
public static Set createSet (Iterator elements, AFunction1 f) throws E {
final Set result = new HashSet<> ();
while (elements.hasNext ()) result.add (f.apply (elements.next ()));
return result;
}
/**
* Create a {@link java.util.Set} from an {@link Iterable}, transforming each element.
*/
public static Set createSet (Iterable elements, AFunction1 f) throws E {
return createSet (elements.iterator (), f);
}
/**
* Returns a string representation of a collection, separating elements with a comma.
*/
public static String mkString(Iterable> iterable) {
return mkString (iterable, ", ");
}
/**
* Returns a string representation of a collection, separating elements with a separator
.
*/
public static String mkString(Iterable> iterable, String separator) {
return mkString(iterable, "", separator, "");
}
/**
* Returns a string representation of a collection, separating elements with a separator
and putting
* prefix
before the first and a suffix
after the last element.
*/
public static String mkString(Iterable> iterable, String prefix, String separator, String suffix) {
final StringBuilder result = new StringBuilder(prefix);
boolean first = true;
for(Object o: iterable) {
if(first) {
first = false;
}
else {
result.append(separator);
}
result.append(o);
}
result.append(suffix);
return result.toString ();
}
/**
* Returns an element of a collection that matches a predicate, if any, or AOption.none() if there is no match.
*/
public static AOption find(Iterable coll, APredicate super T, E> pred) throws E {
for(T o: coll) {
if(pred.apply(o)) {
return AOption.some(o);
}
}
return AOption.none ();
}
/**
* Matches a predicate against collection elements, and returns true iff it matches them all.
*/
public static boolean forAll(Iterable coll, APredicate super T, E> pred) throws E {
for(T o: coll) {
if(!pred.apply(o)) {
return false;
}
}
return true;
}
/**
* Matches a predicate against collection elements, and returns true iff it matches at least one of them.
*/
public static boolean exists(Iterable coll, APredicate super T, E> pred) throws E {
for(T o: coll) {
if(pred.apply(o)) {
return true;
}
}
return false;
}
/**
* Applies a transformation function to all elements of a collection, creating a new collection from the results.
*/
public static Collection map(Iterable coll, AFunction1 super T, ? extends X, E> f) throws E {
final List result = new ArrayList<>();
for(T o: coll) {
result.add(f.apply(o));
}
return result;
}
/**
* Applies a transformation function to all elements of a collection, creating a new collection from the results.
*/
public static List map(List coll, AFunction1 super T, ? extends X, E> f) throws E {
final List result = createEmptyListOfType (coll, true);
for(T o: coll) {
result.add(f.apply(o));
}
return result;
}
/**
* Applies a transformation function to all elements of a collection, creating a new collection from the results.
*/
public static Set map (Set coll, AFunction1 super T, ? extends X, E> f) throws E {
final Set result = createEmptySetOfType(coll, true);
for(T o: coll) {
result.add(f.apply(o));
}
return result;
}
@SuppressWarnings ("unchecked")
private static List createEmptyListOfType (List> original, boolean sameSize) {
if (original instanceof ArrayList) {
return sameSize ? new ArrayList (original.size ()) : new ArrayList ();
}
try {
return original.getClass ().newInstance ();
}
catch (Exception e) {
return sameSize ? new ArrayList (original.size ()) : new ArrayList ();
}
}
@SuppressWarnings ("unchecked")
private static Set createEmptySetOfType (Set> original, boolean sameSize) {
if (original instanceof HashSet) {
return sameSize ? new HashSet (original.size ()) : new HashSet ();
}
try {
return original.getClass ().newInstance ();
}
catch (Exception e) {
return sameSize ? new HashSet (original.size ()) : new HashSet ();
}
}
/**
* Same as map()
, except that the transformation function returns collections and all the results are
* flattened into a single collection.
*/
public static Collection flatMap(Iterable coll, AFunction1 super T, ? extends Iterable, E> f) throws E {
final List result = new ArrayList<>();
for(T o: coll) {
for(X el: f.apply(o)) {
result.add(el);
}
}
return result;
}
/**
* Same as map()
, except that the transformation function returns collections and all the results are
* flattened into a single collection.
*/
public static List flatMapList(Iterable coll, AFunction1 super T, ? extends Iterable, E> f) throws E {
final List result = new ArrayList<>();
for(T o: coll) {
for(X el: f.apply(o)) {
result.add(el);
}
}
return result;
}
/**
* Same as map()
, except that the transformation function returns collections and all the results are
* flattened into a single collection.
*/
public static Set flatMapSet(Iterable coll, AFunction1 super T, ? extends Iterable, E> f) throws E {
final Set result = new HashSet<>();
for(T o: coll) {
for(X el: f.apply(o)) {
result.add(el);
}
}
return result;
}
/**
* Takes a collection of collections and creates a new collection from the elements, leaving out the innermost
* level of collection.
*/
public static Collection flatten(Iterable extends Iterable> coll) {
final List result = new ArrayList<>();
for(Iterable o: coll) {
for(T el: o) {
result.add(el);
}
}
return result;
}
/**
* Takes a collection of collections and creates a new collection from the elements, leaving out the innermost
* level of collection.
*/
public static List flattenList(Iterable extends Iterable> coll) {
final List result = new ArrayList<>();
for(Iterable o: coll) {
for(T el: o) {
result.add(el);
}
}
return result;
}
/**
* Takes a collection of collections and creates a new collection from the elements, leaving out the innermost
* level of collection.
*/
public static Set flattenSet(Iterable extends Iterable> coll) {
final Set result = new HashSet<>();
for(Iterable o: coll) {
for(T el: o) {
result.add(el);
}
}
return result;
}
/**
* Applies a transformation function to all elements of a collection, where the partial function is defined for. Creates a new collection
* of the transformed elements only. So the number of result elements may be less than the number of elements in the source collection.
*/
public static Collection collect (Iterable coll, APartialFunction super T, ? extends X, E> pf) throws E {
final List result = new ArrayList<>();
for (T o: coll) {
if (pf.isDefinedAt (o)) {
result.add (pf.apply (o));
}
}
return result;
}
/**
* Applies a transformation function to all elements of a collection, where the partial function is defined for. Creates a new collection
* of the transformed elements only. So the number of result elements may be less than the number of elements in the source collection.
*/
public static List collect (List coll, APartialFunction super T, ? extends X, E> pf) throws E {
final List result = createEmptyListOfType (coll, true);
for (T o: coll) {
if (pf.isDefinedAt (o)) {
result.add (pf.apply (o));
}
}
return result;
}
/**
* Applies a transformation function to all elements of a collection, where the partial function is defined for. Creates a new collection
* of the transformed elements only. So the number of result elements may be less than the number of elements in the source collection.
*/
public static Set collect (Set coll, APartialFunction super T, ? extends X, E> pf) throws E {
final Set result = createEmptySetOfType(coll, true);
for(T o: coll) {
if (pf.isDefinedAt (o)) {
result.add (pf.apply (o));
}
}
return result;
}
/**
* Matches all elements of a collection against a predicate, creating a new collection from those that match.
*/
public static Collection filter(Iterable coll, APredicate super T, E> pred) throws E {
final List result = new ArrayList<>();
for(T o: coll) {
if(pred.apply(o)) {
result.add(o);
}
}
return result;
}
/**
* Matches all elements of a collection against a predicate, creating a new collection from those that match.
*/
public static List filter(List coll, APredicate super T, E> pred) throws E {
final List result = createEmptyListOfType (coll, false);
for(T o: coll) {
if(pred.apply(o)) {
result.add(o);
}
}
return result;
}
/**
* Matches all elements of a collection against a predicate, creating a new collection from those that match.
*/
public static Set filter(Set coll, APredicate super T, E> pred) throws E {
final Set result = createEmptySetOfType(coll, false);
for(T o: coll) {
if(pred.apply(o)) {
result.add(o);
}
}
return result;
}
/**
* Creates a Map from a collection. Each element's key is determined by applying a function to the element. All
* elements with the same key are stored as that key's value in the returned Map.
*/
public static Map> groupBy (Iterable coll, AFunction1 super T, ? extends X, E> f) throws E {
final Map> result = new HashMap<>();
for(T o: coll) {
final X key = f.apply(o);
Collection perKey = result.get(key);
if(perKey == null) {
perKey = new ArrayList<>();
result.put(key, perKey);
}
perKey.add(o);
}
return result;
}
/**
* Creates a Map from a collection. Each element's key is determined by applying a function to the element. All
* elements with the same key are stored as that key's value in the returned Map.
*/
public static Map> groupBy (List coll, AFunction1 super T, ? extends X, E> f) throws E {
final Map> result = new HashMap<>();
for(T o: coll) {
final X key = f.apply(o);
List perKey = result.get(key);
if(perKey == null) {
perKey = createEmptyListOfType (coll, false);
result.put(key, perKey);
}
perKey.add(o);
}
return result;
}
/**
* Creates a Map from a collection. Each element's key is determined by applying a function to the element. All
* elements with the same key are stored as that key's value in the returned Map.
*/
public static Map> groupBy (Set coll, AFunction1 super T, ? extends X, E> f) throws E {
final Map> result = new HashMap<>();
for(T o: coll) {
final X key = f.apply(o);
Set perKey = result.get(key);
if(perKey == null) {
perKey = createEmptySetOfType(coll, false);
result.put(key, perKey);
}
perKey.add(o);
}
return result;
}
/**
* Creates a Map from a collection. Each element's key is determined by applying a function to the element. All
* elements with the same key are stored as that key's value in the returned Map.
*
* This method gives control over the equalityForEquals strategy used to determine if two keys are 'equal'. To accomodate that,
* the keys are wrapped in AEqualsWrapper.
*
* This method is rather technical in nature, and it is probably more useful as a foundation for generic code than
* for direct use by applications.
*/
public static Map, Collection> groupBy(Iterable coll, AFunction1 super T, ? extends X, E> f, AEquality keyEquality) throws E {
final Map, Collection> result = new HashMap<>();
for(T o: coll) {
final AEqualsWrapper key = new AEqualsWrapper<>(keyEquality, f.apply(o));
Collection perKey = result.get(key);
if(perKey == null) {
perKey = new ArrayList<>();
result.put (key, perKey);
}
perKey.add(o);
}
return result;
}
/**
* Creates a Map from a collection. Each element's key is determined by applying a function to the element. All
* elements with the same key are stored as that key's value in the returned Map.
*
* This method gives control over the equalityForEquals strategy used to determine if two keys are 'equal'. To accomodate that,
* the keys are wrapped in AEqualsWrapper.
*
* This method is rather technical in nature, and it is probably more useful as a foundation for generic code than
* for direct use by applications.
*/
public static Map, List> groupBy(List coll, AFunction1 super T, ? extends X, E> f, AEquality keyEquality) throws E {
final Map, List> result = new HashMap<>();
for(T o: coll) {
final AEqualsWrapper key = new AEqualsWrapper<>(keyEquality, f.apply(o));
List perKey = result.get(key);
if(perKey == null) {
perKey = createEmptyListOfType (coll, false);
result.put (key, perKey);
}
perKey.add(o);
}
return result;
}
/**
* Creates a Map from a collection. Each element's key is determined by applying a function to the element. All
* elements with the same key are stored as that key's value in the returned Map.
*
* This method gives control over the equalityForEquals strategy used to determine if two keys are 'equal'. To accomodate that,
* the keys are wrapped in AEqualsWrapper.
*
* This method is rather technical in nature, and it is probably more useful as a foundation for generic code than
* for direct use by applications.
*/
public static Map, Set> groupBy (Set coll, AFunction1 super T, ? extends X, E> f, AEquality keyEquality) throws E {
final Map, Set> result = new HashMap<>();
for(T o: coll) {
final AEqualsWrapper key = new AEqualsWrapper<>(keyEquality, f.apply(o));
Set perKey = result.get(key);
if(perKey == null) {
perKey = createEmptySetOfType(coll, false);
result.put(key, perKey);
}
perKey.add(o);
}
return result;
}
/**
* Applies a binary operator to a start value and all elements of this sequence, going left to right.
*
* @param element type of the collection
* @param result type
*/
public static R foldLeft (Iterable coll, R startValue, AFunction2 f) throws E {
R result = startValue;
for (T e: coll) {
result = f.apply (result, e);
}
return result;
}
/**
* Applies a binary operator to a start value and all elements of this list, going left to right.
*
* @param element type of the collection
* @param result type
*
*/
public static R foldRight (List coll, R startValue, AFunction2 f) throws E {
R result = startValue;
ListIterator i = coll.listIterator(coll.size());
while ( i.hasPrevious() ) {
result = f.apply (result, i.previous());
}
return result;
}
/**
* Copies the content of an Iterable
into an (immutable) ACollection
instance. Subsequent
* changes to the underlying collection have no effect on the returned ACollection
instance.
*
* The returned collection has list semantics with regard to map()
and other modifying methods;
* duplicate values are allowed.
*/
public static ACollectionWrapper asACollectionCopy(Collection c) {
return asACollectionView(new ArrayList<>(c));
}
/**
* Wraps the content of a java.util.Collection
in an ACollection
instance. While the returned
* instance itself has no mutator methods, changes to the underlying collection are reflected in the wrapping
* ACollection
instance.
*
* The returned collection has list semantics with regard to map()
and other modifying methods; duplicate
* values are allowed.
*/
@SuppressWarnings("unchecked")
public static ACollectionWrapper asACollectionView(Collection c) {
return new ACollectionWrapper(c);
}
/**
* Wraps the content of a java.util.Set
in an ASet
instance. While the returned
* instance itself has no mutator methods, changes to the underlying collection are reflected in the wrapping
* ASet
instance.
*
* The returned collection has set semantics with regard to map()
and other modifying methods; duplicate
* values are removed.
*/
public static ASetWrapper asASetView (Collection c) {
if (c instanceof Set) {
return new ASetWrapper<> ((Set) c);
}
return new ASetWrapper<> (new HashSet<> (c));
}
/**
* Copies the content of an array into an (immutable) ACollection
instance. Subsequent
* changes to the underlying array have no effect on the returned ACollection
instance.
*
* The returned collection has list semantics with regard to map()
and other modifying methods;
* duplicate values are allowed.
*/
@SuppressWarnings("unchecked")
public static AArrayWrapper asArrayCopy(T[] c) {
final T[] newArray = (T[]) Array.newInstance(c.getClass().getComponentType(), c.length);
System.arraycopy(c, 0, newArray, 0, c.length);
return new AArrayWrapper<>(newArray);
}
/**
* Wraps the content of an array in an ACollection
instance. While the returned
* instance itself has no mutator methods, changes to the underlying array are reflected in the wrapping
* ACollection
instance.
*
* The returned collection has list semantics with regard to map()
and other modifying methods; duplicate
* values are allowed.
*/
public static AArrayWrapper asArrayView(T[] c) {
return new AArrayWrapper<>(c);
}
public static class ACollectionWrapper extends AbstractACollection> {
private final Collection inner;
private ACollectionWrapper(Collection inner) {
this.inner = inner;
}
@Override protected ACollectionWrapper createInternal(Collection elements) {
return new ACollectionWrapper<>(elements);
}
@Override protected AEquality equalityForEquals() {
return AEquality.EQUALS;
}
@Override public ACollection clear () {
throw new UnsupportedOperationException ();
}
@Override public boolean contains (T el) {
for (T e: inner) {
if (Objects.equals (e, el)) {
return true;
}
}
return false;
}
@Override public int size() {
return inner.size();
}
@Override public ACollection map(AFunction1 super T, ? extends X, E> f) throws E {
return new ACollectionWrapper<>(ACollectionHelper.map(inner, f));
}
@Override public ACollection flatMap(AFunction1 super T, ? extends Iterable, E> f) throws E {
return new ACollectionWrapper<>(ACollectionHelper.flatMap (inner, f));
}
@SuppressWarnings("unchecked")
@Override public ACollection flatten() {
return new ACollectionWrapper<>(ACollectionHelper.flatten ((Iterable extends Iterable>) inner));
}
@Override public ACollection collect (APartialFunction super T, ? extends X, E> pf) throws E {
return new ACollectionWrapper<>(ACollectionHelper.collect (inner, pf));
}
@Override public R foldLeft (R startValue, AFunction2 f) throws E {
return ACollectionHelper.foldLeft (inner, startValue, f);
}
@SuppressWarnings("NullableProblems")
@Override public Iterator iterator() {
return inner.iterator();
}
}
public static class ASetWrapper extends AbstractACollection> implements ASet {
private final Set inner;
public ASetWrapper (Set inner) {
this.inner = inner;
}
@Override public Set asJavaUtilSet () {
return inner;
}
@Override protected ASetWrapper createInternal (Collection elements) {
return new ASetWrapper<> (new HashSet <> (elements));
}
@Override public ACollection clear () {
return createInternal (new HashSet ());
}
@Override public int size () {
return inner.size ();
}
@Override public boolean contains (T el) {
return inner.contains (el);
}
@Override public Iterator iterator () {
return inner.iterator ();
}
@Override public AEquality equalityForEquals () {
return AEquality.EQUALS;
}
@Override public ASet added (T el) {
throw new UnsupportedOperationException ("adding and removing elements is not supported for views - create a full-blown AHashSet instead");
}
@Override public ASet removed (T el) {
throw new UnsupportedOperationException ("adding and removing elements is not supported for views - create a full-blown AHashSet instead");
}
@Override public ASet map (AFunction1 super T, ? extends X, E> f) throws E {
return new ASetWrapper<> (ACollectionHelper.map (inner, f));
}
@Override public ASet flatMap (AFunction1 super T, ? extends Iterable, E> f) throws E {
return new ASetWrapper<> (new HashSet<> (ACollectionHelper.flatMap (inner, f)));
}
@Override public ASet collect (APartialFunction super T, ? extends X, E> pf) throws E {
return new ASetWrapper<> (ACollectionHelper.collect (inner, pf));
}
@SuppressWarnings ("unchecked")
@Override public ASet flatten () {
return new ASetWrapper<>(new HashSet<> (ACollectionHelper.flatten ((Iterable extends Iterable>) inner)));
}
@Override public ASetWrapper filter (APredicate super T, E> pred) throws E {
return new ASetWrapper<> (ACollectionHelper.filter (inner, pred));
}
}
public static class AArrayWrapper extends AbstractACollection> {
private final T[] inner;
private AArrayWrapper(T[] inner) {
this.inner = inner;
}
@SuppressWarnings("unchecked")
@Override protected AArrayWrapper createInternal(Collection elements) {
final T[] result = (T[]) Array.newInstance(inner.getClass().getComponentType(), elements.size());
int idx = 0;
for(T o: elements) {
result[idx++] = o;
}
return new AArrayWrapper<>(result);
}
@Override public ACollection clear () {
throw new UnsupportedOperationException ();
}
@Override public boolean contains (T el) {
for (T e: inner) {
if (Objects.equals (e, el)) {
return true;
}
}
return false;
}
@Override protected AEquality equalityForEquals() {
return AEquality.EQUALS;
}
@Override public int size() {
return inner.length;
}
/**
* Returns ACollectionWrapper instead of AArrayWrapper because Java can not instantiate an array for a component type that is available only as a generic parameter.
*/
@SuppressWarnings("unchecked")
@Override public ACollectionWrapper flatten() {
return new ACollectionWrapper<>(ACollectionHelper.flatten(Arrays.asList((Iterable[]) inner)));
}
/**
* Returns ACollectionWrapper instead of AArrayWrapper because Java can not instantiate an array for a component type that is available only as a generic parameter.
*/
@Override
public ACollectionWrapper map(AFunction1 super T, ? extends X, E> f) throws E {
return new ACollectionWrapper<>(ACollectionHelper.map (Arrays.asList (inner), f));
}
/**
* Returns ACollectionWrapper instead of AArrayWrapper because Java can not instantiate an array for a component type that is available only as a generic parameter.
*/
@Override
public ACollectionWrapper flatMap(AFunction1 super T, ? extends Iterable, E> f) throws E {
return new ACollectionWrapper<>(ACollectionHelper.flatMap (Arrays.asList (inner), f));
}
@Override
public ACollection collect (APartialFunction super T, ? extends X, E> pf) throws E {
return new ACollectionWrapper<> (ACollectionHelper.collect (Arrays.asList (inner), pf));
}
@Override public R foldLeft (R startValue, AFunction2 f) throws E {
return ACollectionHelper.foldLeft (Arrays.asList (inner), startValue, f);
}
@SuppressWarnings("NullableProblems")
@Override
public Iterator iterator() {
return new Iterator() {
int idx = 0;
@Override public boolean hasNext() {
return idx < inner.length;
}
@Override public T next() {
final T result = inner[idx];
idx += 1;
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
/**
* Returns a Collection
with the exact same elements as an Iterable
, copying only if the parameter is not a collection.
*/
public static List asJavaUtilList(Iterable c) {
if(c instanceof List) {
return (List) c;
}
return asJavaUtilCollection(c.iterator());
}
/**
* Returns a Collection
with the exact same elements as an Iterable
, copying only if the parameter is not a collection.
*/
public static Collection asJavaUtilCollection(Iterable c) {
if(c instanceof Collection) {
return (Collection) c;
}
return asJavaUtilCollection(c.iterator());
}
/**
* Copies the elements from an Iterator
into a Collection
.
*/
public static List asJavaUtilCollection(Iterator c) {
final List result = new ArrayList<>();
while(c.hasNext()) {
result.add(c.next());
}
return result;
}
}