[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>
|
<body>
|
||||||
|
|
||||||
<release version="4.1" date="TBD" description="">
|
<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">
|
<action issue="COLLECTIONS-537" dev="tn" type="fix" due-to="Frank Jakop">
|
||||||
Harmonized signature of factory methods for functor-related classes which take
|
Harmonized signature of factory methods for functor-related classes which take
|
||||||
a collection as input with their array counterparts.
|
a collection as input with their array counterparts.
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.commons.collections4;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -990,6 +991,208 @@ public class CollectionUtils {
|
||||||
return outputCollection;
|
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
|
* Returns a new Collection consisting of the elements of inputCollection
|
||||||
* transformed by the given transformer.
|
* 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.collection.UnmodifiableCollection;
|
||||||
import org.apache.commons.collections4.functors.DefaultEquator;
|
import org.apache.commons.collections4.functors.DefaultEquator;
|
||||||
import org.apache.commons.collections4.queue.CircularFifoQueue;
|
import org.apache.commons.collections4.queue.CircularFifoQueue;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
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
|
//Up to here
|
||||||
@Test
|
@Test
|
||||||
public void filter() {
|
public void filter() {
|
||||||
|
@ -1178,6 +1185,87 @@ public class CollectionUtilsTest extends MockTestCase {
|
||||||
assertTrue(output1.contains(4L));
|
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
|
@Test
|
||||||
public void collect() {
|
public void collect() {
|
||||||
final Transformer<Number, Long> transformer = TransformerUtils.constantTransformer(2L);
|
final Transformer<Number, Long> transformer = TransformerUtils.constantTransformer(2L);
|
||||||
|
|
Loading…
Reference in New Issue