Author: tn
Date: Thu Jan 1 13:51:44 2015
New Revision: 1648844
URL: http://svn.apache.org/r1648844
Log:
[COLLECTIONS-511] Added CollectionUtils.partition(...) methods. Thanks to Brent
Worden, Nathan Blomquist.
Modified:
commons/proper/collections/trunk/src/changes/changes.xml
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
Modified: commons/proper/collections/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/changes/changes.xml?rev=1648844&r1=1648843&r2=1648844&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/changes/changes.xml (original)
+++ commons/proper/collections/trunk/src/changes/changes.xml Thu Jan 1
13:51:44 2015
@@ -22,6 +22,10 @@
<body>
<release version="4.1" date="TBD" description="">
+ <action issue="COLLECTIONS-511" dev="tn" type="add" due-to="Nathan
Blomquist, Brent Worden">
+ Added new methods "CollectionUtils#partition(...)" to partition an input
collection
+ into separate output collections based on evaluation of one or more
predicates.
+ </action>
<action issue="COLLECTIONS-537" dev="tn" type="fix" due-to="Frank Jakop">
Harmonized signature of factory methods for functor-related classes
which take
a collection as input with their array counterparts.
Modified:
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java
URL:
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java?rev=1648844&r1=1648843&r2=1648844&view=diff
==============================================================================
---
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java
(original)
+++
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java
Thu Jan 1 13:51:44 2015
@@ -19,6 +19,7 @@ package org.apache.commons.collections4;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
@@ -991,6 +992,208 @@ public class CollectionUtils {
}
/**
+ * Partitions all elements from inputCollection into separate output
collections,
+ * based on the evaluation of the given predicate.
+ * <p>
+ * For each predicate, the returned list will contain a collection holding
+ * all elements of the input collection matching the predicate. The last
collection
+ * contained in the list will hold all elements which didn't match any
predicate:
+ * <pre>
+ * [C1, R] = partition(I, P1) with
+ * I = input collection
+ * P1 = first predicate
+ * C1 = collection of elements matching P1
+ * R = collection of elements rejected by all predicates
+ * </pre>
+ * <p>
+ * If the input collection is <code>null</code>, an empty list will be
returned.
+ * If the input predicate is <code>null</code>, all elements of the input
collection
+ * will be added to the rejected collection.
+ * <p>
+ * 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 <O> the type of object the {@link Iterable} contains
+ * @param <R> the type of the output {@link Collection}
+ * @param inputCollection the collection to get the input from, may be
null
+ * @param predicate the predicate to use, may be null
+ * @return a list containing the output collections
+ * @since 4.1
+ */
+ public static <O, R extends Collection<O>> List<R> partition(final
Iterable<? extends O> inputCollection,
+ final Predicate<? super O> predicate) {
+
+ @SuppressWarnings("unchecked") // safe
+ final Class<R> outputClass = (Class<R>) ArrayList.class;
+ @SuppressWarnings("unchecked") // safe
+ final Predicate<? super O>[] predicates = new Predicate[] { predicate
};
+ return partition(inputCollection,
FactoryUtils.instantiateFactory(outputClass), predicates);
+ }
+
+ /**
+ * Partitions all elements from inputCollection into an output and
rejected collection,
+ * based on the evaluation of the given predicate.
+ * <p>
+ * Elements matching the predicate are added to the
<code>outputCollection</code>,
+ * all other elements are added to the <code>rejectedCollection</code>.
+ * <p>
+ * If the input predicate is <code>null</code>, no elements are added to
+ * <code>outputCollection</code> or <code>rejectedCollection</code>.
+ * <p>
+ * Note: calling the method is equivalent to the following code snippet:
+ * <pre>
+ * select(inputCollection, predicate, outputCollection);
+ * selectRejected(inputCollection, predicate, rejectedCollection);
+ * </pre>
+ *
+ * @param <O> the type of object the {@link Iterable} contains
+ * @param <R> the type of the output {@link Collection}
+ * @param inputCollection the collection to get the input from, may be
null
+ * @param predicate the predicate to use, may be null
+ * @param outputCollection the collection to output selected elements
into, may not be null if the
+ * inputCollection and predicate are not null
+ * @param rejectedCollection the collection to output rejected elements
into, may not be null if the
+ * inputCollection or predicate are not null
+ * @since 4.1
+ */
+ public static <O, R extends Collection<? super O>> void partition(final
Iterable<? extends O> inputCollection,
+ final Predicate<? super O> predicate, R outputCollection, R
rejectedCollection) {
+
+ if (inputCollection != null && predicate != null) {
+ for (final O element : inputCollection) {
+ if (predicate.evaluate(element)) {
+ outputCollection.add(element);
+ } else {
+ rejectedCollection.add(element);
+ }
+ }
+ }
+ }
+
+ /**
+ * Partitions all elements from inputCollection into separate output
collections,
+ * based on the evaluation of the given predicates.
+ * <p>
+ * For each predicate, the returned list will contain a collection holding
+ * all elements of the input collection matching the predicate. The last
collection
+ * contained in the list will hold all elements which didn't match any
predicate:
+ * <pre>
+ * [C1, C2, R] = partition(I, P1, P2) with
+ * I = input collection
+ * 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
+ * </pre>
+ * <p>
+ * <b>Note</b>: elements are only added to the output collection of the
first matching
+ * predicate, determined by the order of arguments.
+ * <p>
+ * If the input collection is <code>null</code>, an empty list will be
returned.
+ * If the input predicate is <code>null</code>, all elements of the input
collection
+ * will be added to the rejected collection.
+ * <p>
+ * 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 <O> the type of object the {@link Iterable} contains
+ * @param <R> the type of the output {@link Collection}
+ * @param inputCollection the collection to get the input from, may be
null
+ * @param predicates the predicates to use, may be null
+ * @return a list containing the output collections
+ * @since 4.1
+ */
+ public static <O, R extends Collection<O>> List<R> partition(final
Iterable<? extends O> inputCollection,
+ final Predicate<? super O>... predicates) {
+
+ @SuppressWarnings("unchecked") // safe
+ final Class<R> outputClass = (Class<R>) ArrayList.class;
+ return partition(inputCollection,
FactoryUtils.instantiateFactory(outputClass), predicates);
+ }
+
+ /**
+ * Partitions all elements from inputCollection into separate output
collections,
+ * based on the evaluation of the given predicates.
+ * <p>
+ * For each predicate, the returned list will contain a collection holding
+ * all elements of the input collection matching the predicate. The last
collection
+ * contained in the list will hold all elements which didn't match any
predicate:
+ * <pre>
+ * [C1, C2, R] = partition(I, P1, P2) with
+ * I = input collection
+ * 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
+ * </pre>
+ * <p>
+ * <b>Note</b>: elements are only added to the output collection of the
first matching
+ * predicate, determined by the order of arguments.
+ * <p>
+ * If the input collection is <code>null</code>, an empty list will be
returned.
+ * If no predicates have been provided, all elements of the input
collection
+ * will be added to the rejected collection.
+ * <p>
+ * 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 <O> the type of object the {@link Iterable} contains
+ * @param <R> the type of the output {@link Collection}
+ * @param inputCollection 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 be empty
+ * @return a list containing the output collections
+ * @since 4.1
+ */
+ public static <O, R extends Collection<O>> List<R> partition(final
Iterable<? extends O> inputCollection,
+ final Factory<R> partitionFactory, final Predicate<? super O>...
predicates) {
+
+ if (inputCollection == null) {
+ return Collections.emptyList();
+ }
+
+ if (predicates == null || predicates.length < 1) {
+ // return the entire input collection as a single partition
+ final R singlePartition = partitionFactory.create();
+ select(inputCollection, PredicateUtils.truePredicate(),
singlePartition);
+ return Collections.singletonList(singlePartition);
+ }
+
+ // create the empty partitions
+ final int numberOfPredicates = predicates.length;
+ final int numberOfPartitions = numberOfPredicates + 1;
+ final List<R> partitions = new ArrayList<R>(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 : inputCollection) {
+ 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;
+ }
+
+ /**
* Returns a new Collection consisting of the elements of inputCollection
* transformed by the given transformer.
* <p>
Modified:
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java?rev=1648844&r1=1648843&r2=1648844&view=diff
==============================================================================
---
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
(original)
+++
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
Thu Jan 1 13:51:44 2015
@@ -50,6 +50,7 @@ import org.apache.commons.collections4.c
import org.apache.commons.collections4.collection.UnmodifiableCollection;
import org.apache.commons.collections4.functors.DefaultEquator;
import org.apache.commons.collections4.queue.CircularFifoQueue;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -1067,6 +1068,12 @@ public class CollectionUtilsTest extends
}
};
+ private static Predicate<Number> EVEN = new Predicate<Number>() {
+ public boolean evaluate(final Number input) {
+ return input.intValue() % 2 == 0;
+ }
+ };
+
//Up to here
@Test
public void filter() {
@@ -1179,6 +1186,87 @@ public class CollectionUtilsTest extends
}
@Test
+ public void partition() {
+ List<Integer> input = new ArrayList<Integer>();
+ input.add(1);
+ input.add(2);
+ input.add(3);
+ input.add(4);
+ List<Collection<Integer>> partitions =
CollectionUtils.partition(input, EQUALS_TWO);
+ assertEquals(2, partitions.size());
+
+ // first partition contains 2
+ Collection<Integer> partition = partitions.get(0);
+ assertEquals(1, partition.size());
+ assertEquals(2,
CollectionUtils.extractSingleton(partition).intValue());
+
+ // second partition contains 1, 3, and 4
+ Integer[] expected = {1, 3, 4};
+ partition = partitions.get(1);
+ Assert.assertArrayEquals(expected, partition.toArray());
+
+ partitions = CollectionUtils.partition((List<Integer>) null,
EQUALS_TWO);
+ assertTrue(partitions.isEmpty());
+
+ partitions = CollectionUtils.partition(input);
+ assertEquals(1, partitions.size());
+ assertEquals(input, partitions.get(0));
+ }
+
+ @Test
+ public void partitionWithOutputCollections() {
+ List<Integer> input = new ArrayList<Integer>();
+ input.add(1);
+ input.add(2);
+ input.add(3);
+ input.add(4);
+
+ List<Integer> output = new ArrayList<Integer>();
+ List<Integer> rejected = new ArrayList<Integer>();
+
+ CollectionUtils.partition(input, EQUALS_TWO, output, rejected);
+
+ // output contains 2
+ assertEquals(1, output.size());
+ assertEquals(2, CollectionUtils.extractSingleton(output).intValue());
+
+ // rejected contains 1, 3, and 4
+ Integer[] expected = {1, 3, 4};
+ Assert.assertArrayEquals(expected, rejected.toArray());
+
+ output.clear();
+ rejected.clear();
+ CollectionUtils.partition((List<Integer>) null, EQUALS_TWO, output,
rejected);
+ assertTrue(output.isEmpty());
+ assertTrue(rejected.isEmpty());
+ }
+
+ @Test
+ public void partitionMultiplePredicates() {
+ List<Integer> input = new ArrayList<Integer>();
+ input.add(1);
+ input.add(2);
+ input.add(3);
+ input.add(4);
+ List<Collection<Integer>> partitions =
CollectionUtils.partition(input, EQUALS_TWO, EVEN);
+
+ // first partition contains 2
+ Collection<Integer> partition = partitions.get(0);
+ assertEquals(1, partition.size());
+ assertEquals(2, partition.iterator().next().intValue());
+
+ // second partition contains 4
+ partition = partitions.get(1);
+ assertEquals(1, partition.size());
+ assertEquals(4, partition.iterator().next().intValue());
+
+ // third partition contains 1 and 3
+ Integer[] expected = {1, 3};
+ partition = partitions.get(2);
+ Assert.assertArrayEquals(expected, partition.toArray());
+ }
+
+ @Test
public void collect() {
final Transformer<Number, Long> transformer =
TransformerUtils.constantTransformer(2L);
Collection<Number> collection = CollectionUtils.<Integer,
Number>collect(iterableA, transformer);