diff --git a/src/changes/changes.xml b/src/changes/changes.xml index a87900e5a..97ff1465d 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -22,6 +22,9 @@ + + Added "ListUtils#partition" method to split a List into consecutive sublists. + Added serialization support for "FixedOrderComparator" and "TransformingComparator". diff --git a/src/main/java/org/apache/commons/collections/ListUtils.java b/src/main/java/org/apache/commons/collections/ListUtils.java index 3f492bed2..86736f790 100644 --- a/src/main/java/org/apache/commons/collections/ListUtils.java +++ b/src/main/java/org/apache/commons/collections/ListUtils.java @@ -16,6 +16,7 @@ */ package org.apache.commons.collections; +import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -438,5 +439,73 @@ public class ListUtils { } return -1; } + + /** + * Returns consecutive {@link List#subList(int, int) sublists} of a + * list, each of the same size (the final list may be smaller). For example, + * partitioning a list containing {@code [a, b, c, d, e]} with a partition + * size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer list containing + * two inner lists of three and two elements, all in the original order. + *

+ * The outer list is unmodifiable, but reflects the latest state of the + * source list. The inner lists are sublist views of the original list, + * produced on demand using {@link List#subList(int, int)}, and are subject + * to all the usual caveats about modification as explained in that API. + *

+ * Adapted from http://code.google.com/p/guava-libraries/ + * + * @param the element type + * @param list the list to return consecutive sublists of + * @param size the desired size of each sublist (the last may be smaller) + * @return a list of consecutive sublists + * @throws IllegalArgumentException if list is {@code null} or size is not strictly positive + */ + public static List> partition(List list, int size) { + if (list == null) { + throw new IllegalArgumentException("List must not be null"); + } + if (size <= 0) { + throw new IllegalArgumentException("Size must be greater than 0"); + } + return new Partition(list, size); + } + /** + * Provides a partition view on a {@link List}. + */ + private static class Partition extends AbstractList> { + private final List list; + private final int size; + + private Partition(List list, int size) { + this.list = list; + this.size = size; + } + + public List get(int index) { + int listSize = size(); + if (listSize < 0) { + throw new IllegalArgumentException("negative size: " + listSize); + } + if (index < 0) { + throw new IndexOutOfBoundsException("Index " + index + " must not be negative"); + } + if (index >= listSize) { + throw new IndexOutOfBoundsException("Index " + index + " must be less than size " + + listSize); + } + int start = index * size; + int end = Math.min(start + size, list.size()); + return list.subList(start, end); + } + + public int size() { + return (list.size() + size - 1) / size; + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + } } diff --git a/src/test/java/org/apache/commons/collections/TestListUtils.java b/src/test/java/org/apache/commons/collections/TestListUtils.java index 74e070861..402a13605 100644 --- a/src/test/java/org/apache/commons/collections/TestListUtils.java +++ b/src/test/java/org/apache/commons/collections/TestListUtils.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import junit.framework.Assert; import junit.framework.Test; import org.apache.commons.collections.functors.EqualPredicate; @@ -30,12 +31,7 @@ import org.apache.commons.collections.list.PredicatedList; /** * Tests for ListUtils. * - * @version $Revision$ - * - * @author Stephen Colebourne - * @author Neil O'Toole - * @author Matthew Hawthorne - * @author Dave Meikle + * @version $Id$ */ public class TestListUtils extends BulkTest { @@ -297,4 +293,32 @@ public class TestListUtils extends BulkTest { assertEquals(ListUtils.indexOf(fullList, null), -1); } + public void testPartition() { + List strings = new ArrayList(); + for (int i = 0; i <= 6; i++) { + strings.add(i); + } + + final List> partition = ListUtils.partition(strings, 3); + + assertNotNull(partition); + assertEquals(3, partition.size()); + assertEquals(1, partition.get(2).size()); + + try { + ListUtils.partition(null, 3); + Assert.fail("failed to check for null argument"); + } catch (IllegalArgumentException e) {} + + try { + ListUtils.partition(strings, 0); + Assert.fail("failed to check for size argument"); + } catch (IllegalArgumentException e) {} + + try { + ListUtils.partition(strings, -10); + Assert.fail("failed to check for size argument"); + } catch (IllegalArgumentException e) {} + + } }