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 &lt; 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 &lt; 3]
+     * and [x &lt; 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 &lt; 3]
+     * and [x &lt; 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);


Reply via email to