[COLLECTIONS-511] Added CollectionUtils.partition(...) methods. Thanks to Brent Worden, Nathan Blomquist.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1648844 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8cb5cbc601
commit
90f4139bf7
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
@ -990,6 +991,208 @@ public class CollectionUtils {
|
|||
return outputCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.apache.commons.collections4.collection.TransformedCollection;
|
|||
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 MockTestCase {
|
|||
}
|
||||
};
|
||||
|
||||
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() {
|
||||
|
@ -1178,6 +1185,87 @@ public class CollectionUtilsTest extends MockTestCase {
|
|||
assertTrue(output1.contains(4L));
|
||||
}
|
||||
|
||||
@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);
|
||||
|
|
Loading…
Reference in New Issue