I needed something similar so this is my implementation. It allows the caller to specify which implementation of List should be returned:
package com.mrojas.util; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ListUtils { /** * Splits a list into smaller sublists. * The original list remains unmodified and changes on the sublists are not propagated to the original list. * * * @param original * The list to split * @param maxListSize * The max amount of element a sublist can hold. * @param listImplementation * The implementation of List to be used to create the returned sublists * @return A list of sublists * @throws IllegalArgumentException * if the argument maxListSize is zero or a negative number * @throws NullPointerException * if arguments original or listImplementation are null */ public static final <T> List<List<T>> split(final List<T> original, final int maxListSize, final Class<? extends List> listImplementation) { if (maxListSize <= 0) { throw new IllegalArgumentException("maxListSize must be greater than zero"); } final T[] elements = (T[]) original.toArray(); final int maxChunks = (int) Math.ceil(elements.length / (double) maxListSize); final List<List<T>> lists = new ArrayList<List<T>>(maxChunks); for (int i = 0; i < maxChunks; i++) { final int from = i * maxListSize; final int to = Math.min(from + maxListSize, elements.length); final T[] range = Arrays.copyOfRange(elements, from, to); lists.add(createSublist(range, listImplementation)); } return lists; } /** * Splits a list into smaller sublists. The sublists are of type ArrayList. * The original list remains unmodified and changes on the sublists are not propagated to the original list. * * * @param original * The list to split * @param maxListSize * The max amount of element a sublist can hold. * @return A list of sublists */ public static final <T> List<List<T>> split(final List<T> original, final int maxListSize) { return split(original, maxListSize, ArrayList.class); } private static <T> List<T> createSublist(final T[] elements, final Class<? extends List> listImplementation) { List<T> sublist; final List<T> asList = Arrays.asList(elements); try { sublist = listImplementation.newInstance(); sublist.addAll(asList); } catch (final InstantiationException e) { sublist = asList; } catch (final IllegalAccessException e) { sublist = asList; } return sublist; }
}
And some test cases:
package com.mrojas.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.junit.Test; public class ListUtilsTest { @Test public void evenSplitTest() { final List<List<Object>> sublists = ListUtils.split(getPopulatedList(10), 2, LinkedList.class); assertEquals(5, sublists.size()); for (final List<Object> sublist : sublists) { assertEquals(2, sublist.size()); assertTrue(sublist instanceof LinkedList<?>); } } @Test public void unevenSplitTest() { final List<List<Object>> sublists = ListUtils.split(getPopulatedList(10), 3, LinkedList.class); assertEquals(4, sublists.size()); assertEquals(3, sublists.get(0).size()); assertEquals(3, sublists.get(1).size()); assertEquals(3, sublists.get(2).size()); assertEquals(1, sublists.get(3).size()); } @Test public void greaterThanSizeSplitTest() { final List<List<Object>> sublists = ListUtils.split(getPopulatedList(10), 20, LinkedList.class); assertEquals(1, sublists.size()); assertEquals(10, sublists.get(0).size()); } @Test public void emptyListSplitTest() { final List<List<Object>> sublists = ListUtils.split(Collections.emptyList(), 10, LinkedList.class); assertEquals(0, sublists.size()); } @Test(expected=IllegalArgumentException.class) public void negativeChunkSizeTest() { ListUtils.split(getPopulatedList(5), -10, LinkedList.class); } @Test public void invalidClassTest() { final List<List<Object>> sublists = ListUtils.split(getPopulatedList(10), 2, LinkedList.class); assertEquals(5, sublists.size()); for (final List<Object> sublist : sublists) { assertEquals(2, sublist.size()); assertTrue(sublist instanceof LinkedList<?>); } } private List<Object> getPopulatedList(final int size) { final List<Object> list = new ArrayList<Object>(10); for (int i = 0; i < 10; i++) { list.add(new Object()); } return list; }
}