All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ajjpj.afoundation.collection.ACollectionHelper Maven / Gradle / Ivy

The newest version!
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 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 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 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 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 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 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, 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, 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, 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> 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> 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> 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 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 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 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 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 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 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 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 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 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 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 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 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 f) throws E { return new ACollectionWrapper<>(ACollectionHelper.map(inner, f)); } @Override public ACollection flatMap(AFunction1, E> f) throws E { return new ACollectionWrapper<>(ACollectionHelper.flatMap (inner, f)); } @SuppressWarnings("unchecked") @Override public ACollection flatten() { return new ACollectionWrapper<>(ACollectionHelper.flatten ((Iterable>) inner)); } @Override public ACollection collect (APartialFunction 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 f) throws E { return new ASetWrapper<> (ACollectionHelper.map (inner, f)); } @Override public ASet flatMap (AFunction1, E> f) throws E { return new ASetWrapper<> (new HashSet<> (ACollectionHelper.flatMap (inner, f))); } @Override public ASet collect (APartialFunction pf) throws E { return new ASetWrapper<> (ACollectionHelper.collect (inner, pf)); } @SuppressWarnings ("unchecked") @Override public ASet flatten () { return new ASetWrapper<>(new HashSet<> (ACollectionHelper.flatten ((Iterable>) inner))); } @Override public ASetWrapper filter (APredicate 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 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, E> f) throws E { return new ACollectionWrapper<>(ACollectionHelper.flatMap (Arrays.asList (inner), f)); } @Override public ACollection collect (APartialFunction 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy