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

org.apache.commons.collections4.IterableUtils Maven / Gradle / Ivy

Go to download

The Apache Commons Collections package contains types that extend and augment the Java Collections Framework.

There is a newer version: 4.5.0-M1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache 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://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.apache.commons.collections4;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections4.functors.EqualPredicate;
import org.apache.commons.collections4.iterators.LazyIteratorChain;
import org.apache.commons.collections4.iterators.ReverseListIterator;
import org.apache.commons.collections4.iterators.UniqueFilterIterator;

/**
 * Provides utility methods and decorators for {@link Iterable} instances.
 * 

* Note: this util class has been designed for fail-fast argument checking. *

*
    *
  • * all decorator methods are NOT null-safe wrt the provided Iterable argument, i.e. * they will throw a {@link NullPointerException} if a null Iterable is passed as argument. *
  • * all other utility methods are null-safe wrt the provided Iterable argument, i.e. they will * treat a null Iterable the same way as an empty one. Other arguments which are null, * e.g. a {@link Predicate}, will result in a {@link NullPointerException}. Exception: passing * a null {@link Comparator} is equivalent to a Comparator with natural ordering. *
* * @since 4.1 */ public class IterableUtils { /** * An empty iterable. */ @SuppressWarnings("rawtypes") static final FluentIterable EMPTY_ITERABLE = new FluentIterable() { @Override public Iterator iterator() { return IteratorUtils.emptyIterator(); } }; // Empty // ---------------------------------------------------------------------- /** * Gets an empty iterable. *

* This iterable does not contain any elements. * * @param the element type * @return an empty iterable */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Iterable emptyIterable() { return EMPTY_ITERABLE; } // Chained // ---------------------------------------------------------------------- /** * Combines two iterables into a single iterable. *

* The returned iterable has an iterator that traverses the elements in {@code a}, * followed by the elements in {@code b}. The source iterators are not polled until * necessary. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the element type * @param a the first iterable, may not be null * @param b the second iterable, may not be null * @return a new iterable, combining the provided iterables * @throws NullPointerException if either a or b is null */ @SuppressWarnings("unchecked") public static Iterable chainedIterable(final Iterable a, final Iterable b) { return chainedIterable(new Iterable[] {a, b}); } /** * Combines three iterables into a single iterable. *

* The returned iterable has an iterator that traverses the elements in {@code a}, * followed by the elements in {@code b} and {@code c}. The source iterators are * not polled until necessary. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the element type * @param a the first iterable, may not be null * @param b the second iterable, may not be null * @param c the third iterable, may not be null * @return a new iterable, combining the provided iterables * @throws NullPointerException if either of the provided iterables is null */ @SuppressWarnings("unchecked") public static Iterable chainedIterable(final Iterable a, final Iterable b, final Iterable c) { return chainedIterable(new Iterable[] {a, b, c}); } /** * Combines four iterables into a single iterable. *

* The returned iterable has an iterator that traverses the elements in {@code a}, * followed by the elements in {@code b}, {@code c} and {@code d}. The source * iterators are not polled until necessary. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the element type * @param a the first iterable, may not be null * @param b the second iterable, may not be null * @param c the third iterable, may not be null * @param d the fourth iterable, may not be null * @return a new iterable, combining the provided iterables * @throws NullPointerException if either of the provided iterables is null */ @SuppressWarnings("unchecked") public static Iterable chainedIterable(final Iterable a, final Iterable b, final Iterable c, final Iterable d) { return chainedIterable(new Iterable[] {a, b, c, d}); } /** * Combines the provided iterables into a single iterable. *

* The returned iterable has an iterator that traverses the elements in the order * of the arguments, i.e. iterables[0], iterables[1], .... The source iterators * are not polled until necessary. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the element type * @param iterables the iterables to combine, may not be null * @return a new iterable, combining the provided iterables * @throws NullPointerException if either of the provided iterables is null */ public static Iterable chainedIterable(final Iterable... iterables) { checkNotNull(iterables); return new FluentIterable() { @Override public Iterator iterator() { return new LazyIteratorChain() { @Override protected Iterator nextIterator(final int count) { if (count > iterables.length) { return null; } return iterables[count - 1].iterator(); } }; } }; } // Collated // ---------------------------------------------------------------------- /** * Combines the two provided iterables into an ordered iterable using * natural ordering. *

* The returned iterable's iterator supports {@code remove()} when the * corresponding input iterator supports it. * * @param the element type * @param a the first iterable, may not be null * @param b the second iterable, may not be null * @return a filtered view on the specified iterable * @throws NullPointerException if either of the provided iterables is null */ public static Iterable collatedIterable(final Iterable a, final Iterable b) { checkNotNull(a, b); return new FluentIterable() { @Override public Iterator iterator() { return IteratorUtils.collatedIterator(null, a.iterator(), b.iterator()); } }; } /** * Combines the two provided iterables into an ordered iterable using the * provided comparator. If the comparator is null, natural ordering will be * used. *

* The returned iterable's iterator supports {@code remove()} when the * corresponding input iterator supports it. * * @param the element type * @param comparator the comparator defining an ordering over the elements, * may be null, in which case natural ordering will be used * @param a the first iterable, may not be null * @param b the second iterable, may not be null * @return a filtered view on the specified iterable * @throws NullPointerException if either of the provided iterables is null */ public static Iterable collatedIterable(final Comparator comparator, final Iterable a, final Iterable b) { checkNotNull(a, b); return new FluentIterable() { @Override public Iterator iterator() { return IteratorUtils.collatedIterator(comparator, a.iterator(), b.iterator()); } }; } // Filtered // ---------------------------------------------------------------------- /** * Returns a view of the given iterable that only contains elements matching * the provided predicate. *

* The returned iterable's iterator supports {@code remove()} when the * corresponding input iterator supports it. * * @param the element type * @param iterable the iterable to filter, may not be null * @param predicate the predicate used to filter elements, may not be null * @return a filtered view on the specified iterable * @throws NullPointerException if either iterable or predicate is null */ public static Iterable filteredIterable(final Iterable iterable, final Predicate predicate) { checkNotNull(iterable); if (predicate == null) { throw new NullPointerException("Predicate must not be null."); } return new FluentIterable() { @Override public Iterator iterator() { return IteratorUtils.filteredIterator(emptyIteratorIfNull(iterable), predicate); } }; } // Bounded // ---------------------------------------------------------------------- /** * Returns a view of the given iterable that contains at most the given number * of elements. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the element type * @param iterable the iterable to limit, may not be null * @param maxSize the maximum number of elements, must not be negative * @return a bounded view on the specified iterable * @throws IllegalArgumentException if maxSize is negative * @throws NullPointerException if iterable is null */ public static Iterable boundedIterable(final Iterable iterable, final long maxSize) { checkNotNull(iterable); if (maxSize < 0) { throw new IllegalArgumentException("MaxSize parameter must not be negative."); } return new FluentIterable() { @Override public Iterator iterator() { return IteratorUtils.boundedIterator(iterable.iterator(), maxSize); } }; } // Looping // ---------------------------------------------------------------------- /** * Returns a view of the given iterable which will cycle infinitely over * its elements. *

* The returned iterable's iterator supports {@code remove()} if * {@code iterable.iterator()} does. After {@code remove()} is called, subsequent * cycles omit the removed element, which is no longer in {@code iterable}. The * iterator's {@code hasNext()} method returns {@code true} until {@code iterable} * is empty. * * @param the element type * @param iterable the iterable to loop, may not be null * @return a view of the iterable, providing an infinite loop over its elements * @throws NullPointerException if iterable is null */ public static Iterable loopingIterable(final Iterable iterable) { checkNotNull(iterable); return new FluentIterable() { @Override public Iterator iterator() { return new LazyIteratorChain() { @Override protected Iterator nextIterator(final int count) { if (IterableUtils.isEmpty(iterable)) { return null; } return iterable.iterator(); } }; } }; } // Reversed // ---------------------------------------------------------------------- /** * Returns a reversed view of the given iterable. *

* In case the provided iterable is a {@link List} instance, a * {@link ReverseListIterator} will be used to reverse the traversal * order, otherwise an intermediate {@link List} needs to be created. *

* The returned iterable's iterator supports {@code remove()} if the * provided iterable is a {@link List} instance. * * @param the element type * @param iterable the iterable to use, may not be null * @return a reversed view of the specified iterable * @throws NullPointerException if iterable is null * @see ReverseListIterator */ public static Iterable reversedIterable(final Iterable iterable) { checkNotNull(iterable); return new FluentIterable() { @Override public Iterator iterator() { final List list = (iterable instanceof List) ? (List) iterable : IteratorUtils.toList(iterable.iterator()); return new ReverseListIterator<>(list); } }; } // Skipping // ---------------------------------------------------------------------- /** * Returns a view of the given iterable that skips the first N elements. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the element type * @param iterable the iterable to use, may not be null * @param elementsToSkip the number of elements to skip from the start, must not be negative * @return a view of the specified iterable, skipping the first N elements * @throws IllegalArgumentException if elementsToSkip is negative * @throws NullPointerException if iterable is null */ public static Iterable skippingIterable(final Iterable iterable, final long elementsToSkip) { checkNotNull(iterable); if (elementsToSkip < 0) { throw new IllegalArgumentException("ElementsToSkip parameter must not be negative."); } return new FluentIterable() { @Override public Iterator iterator() { return IteratorUtils.skippingIterator(iterable.iterator(), elementsToSkip); } }; } // Transformed // ---------------------------------------------------------------------- /** * Returns a transformed view of the given iterable where all of its elements * have been transformed by the provided transformer. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the input element type * @param the output element type * @param iterable the iterable to transform, may not be null * @param transformer the transformer, must not be null * @return a transformed view of the specified iterable * @throws NullPointerException if either iterable or transformer is null */ public static Iterable transformedIterable(final Iterable iterable, final Transformer transformer) { checkNotNull(iterable); if (transformer == null) { throw new NullPointerException("Transformer must not be null."); } return new FluentIterable() { @Override public Iterator iterator() { return IteratorUtils.transformedIterator(iterable.iterator(), transformer); } }; } // Unique // ---------------------------------------------------------------------- /** * Returns a unique view of the given iterable. *

* The returned iterable's iterator supports {@code remove()} when the * corresponding input iterator supports it. Calling {@code remove()} * will only remove a single element from the underlying iterator. * * @param the element type * @param iterable the iterable to use, may not be null * @return a unique view of the specified iterable * @throws NullPointerException if iterable is null */ public static Iterable uniqueIterable(final Iterable iterable) { checkNotNull(iterable); return new FluentIterable() { @Override public Iterator iterator() { return new UniqueFilterIterator<>(iterable.iterator()); } }; } // Unmodifiable // ---------------------------------------------------------------------- /** * Returns an unmodifiable view of the given iterable. *

* The returned iterable's iterator does not support {@code remove()}. * * @param the element type * @param iterable the iterable to use, may not be null * @return an unmodifiable view of the specified iterable * @throws NullPointerException if iterable is null */ public static Iterable unmodifiableIterable(final Iterable iterable) { checkNotNull(iterable); if (iterable instanceof UnmodifiableIterable) { return iterable; } return new UnmodifiableIterable<>(iterable); } /** * Inner class to distinguish unmodifiable instances. */ private static final class UnmodifiableIterable extends FluentIterable { private final Iterable unmodifiable; public UnmodifiableIterable(final Iterable iterable) { super(); this.unmodifiable = iterable; } @Override public Iterator iterator() { return IteratorUtils.unmodifiableIterator(unmodifiable.iterator()); } } // Zipping // ---------------------------------------------------------------------- /** * Interleaves two iterables into a single iterable. *

* The returned iterable has an iterator that traverses the elements in {@code a} * and {@code b} in alternating order. The source iterators are not polled until * necessary. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the element type * @param a the first iterable, may not be null * @param b the second iterable, may not be null * @return a new iterable, interleaving the provided iterables * @throws NullPointerException if either a or b is null */ public static Iterable zippingIterable(final Iterable a, final Iterable b) { checkNotNull(a); checkNotNull(b); return new FluentIterable() { @Override public Iterator iterator() { return IteratorUtils.zippingIterator(a.iterator(), b.iterator()); } }; } /** * Interleaves two iterables into a single iterable. *

* The returned iterable has an iterator that traverses the elements in {@code a} * and {@code b} in alternating order. The source iterators are not polled until * necessary. *

* The returned iterable's iterator supports {@code remove()} when the corresponding * input iterator supports it. * * @param the element type * @param first the first iterable, may not be null * @param others the array of iterables to interleave, may not be null * @return a new iterable, interleaving the provided iterables * @throws NullPointerException if either of the provided iterables is null */ public static Iterable zippingIterable(final Iterable first, final Iterable... others) { checkNotNull(first); checkNotNull(others); return new FluentIterable() { @Override public Iterator iterator() { @SuppressWarnings("unchecked") // safe final Iterator[] iterators = new Iterator[others.length + 1]; iterators[0] = first.iterator(); for (int i = 0; i < others.length; i++) { iterators[i + 1] = others[i].iterator(); } return IteratorUtils.zippingIterator(iterators); } }; } // Utility methods // ---------------------------------------------------------------------- /** * Returns an immutable empty iterable if the argument is null, * or the argument itself otherwise. * * @param the element type * @param iterable the iterable, may be null * @return an empty iterable if the argument is null */ public static Iterable emptyIfNull(final Iterable iterable) { return iterable == null ? IterableUtils.emptyIterable() : iterable; } /** * Applies the closure to each element of the provided iterable. * * @param the element type * @param iterable the iterator to use, may be null * @param closure the closure to apply to each element, may not be null * @throws NullPointerException if closure is null */ public static void forEach(final Iterable iterable, final Closure closure) { IteratorUtils.forEach(emptyIteratorIfNull(iterable), closure); } /** * Executes the given closure on each but the last element in the iterable. *

* If the input iterable is null no change is made. * * @param the type of object the {@link Iterable} contains * @param iterable the iterable to get the input from, may be null * @param closure the closure to perform, may not be null * @return the last element in the iterable, or null if iterable is null or empty */ public static E forEachButLast(final Iterable iterable, final Closure closure) { return IteratorUtils.forEachButLast(emptyIteratorIfNull(iterable), closure); } /** * Finds the first element in the given iterable which matches the given predicate. *

* A null or empty iterator returns null. * * @param the element type * @param iterable the iterable to search, may be null * @param predicate the predicate to use, may not be null * @return the first element of the iterable which matches the predicate or null if none could be found * @throws NullPointerException if predicate is null */ public static E find(final Iterable iterable, final Predicate predicate) { return IteratorUtils.find(emptyIteratorIfNull(iterable), predicate); } /** * Returns the index of the first element in the specified iterable that * matches the given predicate. *

* A null or empty iterable returns -1. * * @param the element type * @param iterable the iterable to search, may be null * @param predicate the predicate to use, may not be null * @return the index of the first element which matches the predicate or -1 if none matches * @throws NullPointerException if predicate is null */ public static int indexOf(final Iterable iterable, final Predicate predicate) { return IteratorUtils.indexOf(emptyIteratorIfNull(iterable), predicate); } /** * Answers true if a predicate is true for every element of an iterable. *

* A null or empty iterable returns true. * * @param the type of object the {@link Iterable} contains * @param iterable the {@link Iterable} to use, may be null * @param predicate the predicate to use, may not be null * @return true if every element of the collection matches the predicate or if the * collection is empty, false otherwise * @throws NullPointerException if predicate is null */ public static boolean matchesAll(final Iterable iterable, final Predicate predicate) { return IteratorUtils.matchesAll(emptyIteratorIfNull(iterable), predicate); } /** * Answers true if a predicate is true for any element of the iterable. *

* A null or empty iterable returns false. * * @param the type of object the {@link Iterable} contains * @param iterable the {@link Iterable} to use, may be null * @param predicate the predicate to use, may not be null * @return true if any element of the collection matches the predicate, false otherwise * @throws NullPointerException if predicate is null */ public static boolean matchesAny(final Iterable iterable, final Predicate predicate) { return IteratorUtils.matchesAny(emptyIteratorIfNull(iterable), predicate); } /** * Counts the number of elements in the input iterable that match the predicate. *

* A null iterable matches no elements. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may not be null * @return the number of matches for the predicate in the collection * @throws NullPointerException if predicate is null */ public static long countMatches(final Iterable input, final Predicate predicate) { if (predicate == null) { throw new NullPointerException("Predicate must not be null."); } return size(filteredIterable(emptyIfNull(input), predicate)); } /** * Answers true if the provided iterable is empty. *

* A null iterable returns true. * * @param iterable the {@link Iterable to use}, may be null * @return true if the iterable is null or empty, false otherwise */ public static boolean isEmpty(final Iterable iterable) { if (iterable instanceof Collection) { return ((Collection) iterable).isEmpty(); } return IteratorUtils.isEmpty(emptyIteratorIfNull(iterable)); } /** * Checks if the object is contained in the given iterable. *

* A null or empty iterable returns false. * * @param the type of object the {@link Iterable} contains * @param iterable the iterable to check, may be null * @param object the object to check * @return true if the object is contained in the iterable, false otherwise */ public static boolean contains(final Iterable iterable, final Object object) { if (iterable instanceof Collection) { return ((Collection) iterable).contains(object); } return IteratorUtils.contains(emptyIteratorIfNull(iterable), object); } /** * Checks if the object is contained in the given iterable. Object equality * is tested with an {@code equator} unlike {@link #contains(Iterable, Object)} * which uses {@link Object#equals(Object)}. *

* A null or empty iterable returns false. * A null object will not be passed to the equator, instead a * {@link org.apache.commons.collections4.functors.NullPredicate NullPredicate} * will be used. * * @param the type of object the {@link Iterable} contains * @param iterable the iterable to check, may be null * @param object the object to check * @param equator the equator to use to check, may not be null * @return true if the object is contained in the iterable, false otherwise * @throws NullPointerException if equator is null */ public static boolean contains(final Iterable iterable, final E object, final Equator equator) { if (equator == null) { throw new NullPointerException("Equator must not be null."); } return matchesAny(iterable, EqualPredicate.equalPredicate(object, equator)); } /** * Returns the number of occurrences of the provided object in the iterable. * * @param the element type that the {@link Iterable} may contain * @param the element type of the object to find * @param iterable the {@link Iterable} to search * @param obj the object to find the cardinality of * @return the number of occurrences of obj in iterable */ public static int frequency(final Iterable iterable, final T obj) { if (iterable instanceof Set) { return ((Set) iterable).contains(obj) ? 1 : 0; } if (iterable instanceof Bag) { return ((Bag) iterable).getCount(obj); } return size(filteredIterable(emptyIfNull(iterable), EqualPredicate.equalPredicate(obj))); } /** * Returns the index-th value in the iterable's {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* If the {@link Iterable} is a {@link List}, then it will use {@link List#get(int)}. * * @param the type of object in the {@link Iterable}. * @param iterable the {@link Iterable} to get a value from, may be null * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static T get(final Iterable iterable, final int index) { CollectionUtils.checkIndexBounds(index); if (iterable instanceof List) { return ((List) iterable).get(index); } return IteratorUtils.get(emptyIteratorIfNull(iterable), index); } /** * Shortcut for {@code get(iterator, 0)}. *

* Returns the first value in the iterable's {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

*

* If the {@link Iterable} is a {@link List}, then it will use {@link List#get(int)}. *

* * @param the type of object in the {@link Iterable}. * @param iterable the {@link Iterable} to get a value from, may be null * @return the first object * @throws IndexOutOfBoundsException if the request is invalid * @since 4.2 */ public static T first(final Iterable iterable) { return get(iterable, 0); } /** * Returns the number of elements contained in the given iterator. *

* A null or empty iterator returns {@code 0}. * * @param iterable the iterable to check, may be null * @return the number of elements contained in the iterable */ public static int size(final Iterable iterable) { if (iterable instanceof Collection) { return ((Collection) iterable).size(); } return IteratorUtils.size(emptyIteratorIfNull(iterable)); } /** * Partitions all elements from iterable into separate output collections, * based on the evaluation of the given predicate. *

* For each predicate, the result will contain a list holding all elements of the * input iterable matching the predicate. The last list will hold all elements * which didn't match any predicate: *

     *  [C1, R] = partition(I, P1) with
     *  I = input
     *  P1 = first predicate
     *  C1 = collection of elements matching P1
     *  R = collection of elements rejected by all predicates
     * 
*

* If the input iterable is null, the same is returned as for an * empty iterable. *

* Example: for an input list [1, 2, 3, 4, 5] calling partition with a predicate [x < 3] * will result in the following output: [[1, 2], [3, 4, 5]]. * * @param the type of object the {@link Iterable} contains * @param iterable the iterable to partition, may be null * @param predicate the predicate to use, may not be null * @return a list containing the output collections * @throws NullPointerException if predicate is null */ public static List> partition(final Iterable iterable, final Predicate predicate) { if (predicate == null) { throw new NullPointerException("Predicate must not be null."); } @SuppressWarnings({ "unchecked", "rawtypes" }) // safe final Factory> factory = FactoryUtils.instantiateFactory((Class) ArrayList.class); @SuppressWarnings("unchecked") // safe final Predicate[] predicates = new Predicate[] { predicate }; return partition(iterable, factory, predicates); } /** * Partitions all elements from iterable into separate output collections, * based on the evaluation of the given predicates. *

* For each predicate, the result will contain a list holding all elements of the * input iterable matching the predicate. The last list will hold all elements * which didn't match any predicate: *

     *  [C1, C2, R] = partition(I, P1, P2) with
     *  I = input
     *  P1 = first predicate
     *  P2 = second predicate
     *  C1 = collection of elements matching P1
     *  C2 = collection of elements matching P2
     *  R = collection of elements rejected by all predicates
     * 
*

* Note: elements are only added to the output collection of the first matching * predicate, determined by the order of arguments. *

* If the input iterable is null, the same is returned as for an * empty iterable. *

* Example: for an input list [1, 2, 3, 4, 5] calling partition with predicates [x < 3] * and [x < 5] will result in the following output: [[1, 2], [3, 4], [5]]. * * @param the type of object the {@link Iterable} contains * @param iterable the collection to get the input from, may be null * @param predicates the predicates to use, may not be null * @return a list containing the output collections * @throws NullPointerException if any predicate is null */ public static List> partition(final Iterable iterable, final Predicate... predicates) { @SuppressWarnings({ "unchecked", "rawtypes" }) // safe final Factory> factory = FactoryUtils.instantiateFactory((Class) ArrayList.class); return partition(iterable, factory, predicates); } /** * Partitions all elements from iterable into separate output collections, * based on the evaluation of the given predicates. *

* For each predicate, the returned list will contain a collection holding * all elements of the input iterable matching the predicate. The last collection * contained in the list will hold all elements which didn't match any predicate: *

     *  [C1, C2, R] = partition(I, P1, P2) with
     *  I = input
     *  P1 = first predicate
     *  P2 = second predicate
     *  C1 = collection of elements matching P1
     *  C2 = collection of elements matching P2
     *  R = collection of elements rejected by all predicates
     * 
*

* Note: elements are only added to the output collection of the first matching * predicate, determined by the order of arguments. *

* If the input iterable is null, the same is returned as for an * empty iterable. * If no predicates have been provided, all elements of the input collection * will be added to the rejected collection. *

* Example: for an input list [1, 2, 3, 4, 5] calling partition with predicates [x < 3] * and [x < 5] will result in the following output: [[1, 2], [3, 4], [5]]. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param iterable the collection to get the input from, may be null * @param partitionFactory the factory used to create the output collections * @param predicates the predicates to use, may not be null * @return a list containing the output collections * @throws NullPointerException if any predicate is null */ public static > List partition(final Iterable iterable, final Factory partitionFactory, final Predicate... predicates) { if (iterable == null) { final Iterable empty = emptyIterable(); return partition(empty, partitionFactory, predicates); } if (predicates == null) { throw new NullPointerException("Predicates must not be null."); } for (final Predicate p : predicates) { if (p == null) { throw new NullPointerException("Predicate must not be null."); } } if (predicates.length < 1) { // return the entire input collection as a single partition final R singlePartition = partitionFactory.create(); CollectionUtils.addAll(singlePartition, iterable); return Collections.singletonList(singlePartition); } // create the empty partitions final int numberOfPredicates = predicates.length; final int numberOfPartitions = numberOfPredicates + 1; final List partitions = new ArrayList<>(numberOfPartitions); for (int i = 0; i < numberOfPartitions; ++i) { partitions.add(partitionFactory.create()); } // for each element in inputCollection: // find the first predicate that evaluates to true. // if there is a predicate, add the element to the corresponding partition. // if there is no predicate, add it to the last, catch-all partition. for (final O element : iterable) { boolean elementAssigned = false; for (int i = 0; i < numberOfPredicates; ++i) { if (predicates[i].evaluate(element)) { partitions.get(i).add(element); elementAssigned = true; break; } } if (!elementAssigned) { // no predicates evaluated to true // add element to last partition partitions.get(numberOfPredicates).add(element); } } return partitions; } /** * Gets a new list with the contents of the provided iterable. * * @param the element type * @param iterable the iterable to use, may be null * @return a list of the iterator contents */ public static List toList(final Iterable iterable) { return IteratorUtils.toList(emptyIteratorIfNull(iterable)); } /** * Returns a string representation of the elements of the specified iterable. *

* The string representation consists of a list of the iterable's elements, * enclosed in square brackets ({@code "[]"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). Elements are * converted to strings as by {@code String.valueOf(Object)}. * * @param the element type * @param iterable the iterable to convert to a string, may be null * @return a string representation of {@code iterable} */ public static String toString(final Iterable iterable) { return IteratorUtils.toString(emptyIteratorIfNull(iterable)); } /** * Returns a string representation of the elements of the specified iterable. *

* The string representation consists of a list of the iterable's elements, * enclosed in square brackets ({@code "[]"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). Elements are * converted to strings as by using the provided {@code transformer}. * * @param the element type * @param iterable the iterable to convert to a string, may be null * @param transformer the transformer used to get a string representation of an element * @return a string representation of {@code iterable} * @throws NullPointerException if {@code transformer} is null */ public static String toString(final Iterable iterable, final Transformer transformer) { if (transformer == null) { throw new NullPointerException("Transformer must not be null."); } return IteratorUtils.toString(emptyIteratorIfNull(iterable), transformer); } /** * Returns a string representation of the elements of the specified iterable. *

* The string representation consists of a list of the iterable's elements, * enclosed by the provided {@code prefix} and {@code suffix}. Adjacent elements * are separated by the provided {@code delimiter}. Elements are converted to * strings as by using the provided {@code transformer}. * * @param the element type * @param iterable the iterable to convert to a string, may be null * @param transformer the transformer used to get a string representation of an element * @param delimiter the string to delimit elements * @param prefix the prefix, prepended to the string representation * @param suffix the suffix, appended to the string representation * @return a string representation of {@code iterable} * @throws NullPointerException if either transformer, delimiter, prefix or suffix is null */ public static String toString(final Iterable iterable, final Transformer transformer, final String delimiter, final String prefix, final String suffix) { return IteratorUtils.toString(emptyIteratorIfNull(iterable), transformer, delimiter, prefix, suffix); } // Helper methods // ---------------------------------------------------------------------- /** * Fail-fast check for null arguments. * * @param iterable the iterable to check * @throws NullPointerException if iterable is null */ static void checkNotNull(final Iterable iterable) { if (iterable == null) { throw new NullPointerException("Iterable must not be null."); } } /** * Fail-fast check for null arguments. * * @param iterables the iterables to check * @throws NullPointerException if the argument or any of its contents is null */ static void checkNotNull(final Iterable... iterables) { if (iterables == null) { throw new NullPointerException("Iterables must not be null."); } for (final Iterable iterable : iterables) { checkNotNull(iterable); } } /** * Returns an empty iterator if the argument is null, * or {@code iterable.iterator()} otherwise. * * @param the element type * @param iterable the iterable, possibly null * @return an empty iterator if the argument is null */ private static Iterator emptyIteratorIfNull(final Iterable iterable) { return iterable != null ? iterable.iterator() : IteratorUtils.emptyIterator(); } }