Reputation: 55989
What's the simplest, most standard, and/or most efficient way to split a List into two sub-Lists in Java? It's OK to mutate the original List, so no copying should be necessary. The method signature could be
/** Split a list into two sublists. The original list will be modified to
* have size i and will contain exactly the same elements at indices 0
* through i-1 as it had originally; the returned list will have size
* len-i (where len is the size of the original list before the call)
* and will have the same elements at indices 0 through len-(i+1) as
* the original list had at indices i through len-1.
*/
<T> List<T> split(List<T> list, int i);
[EDIT] List.subList
returns a view on the original list, which becomes invalid if the original is modified. So split
can't use subList
unless it also dispenses with the original reference (or, as in Marc Novakowski's answer, uses subList
but immediately copies the result).
Upvotes: 37
Views: 111250
Reputation: 1
List<T> one = orig.stream().limit(i).toList();
List<T> two = orig.stream().skip(i).toList();
You can't necessarily assume that a List will be mutable, and doing a defensive copy will negate any potential gains you'd have by modifying in-place.
Upvotes: 0
Reputation: 11
sample java code to split List
public List<List<Long>> split(List<Long> list, int i ){
List<List<Long>> out = new ArrayList<List<Long>>();
int size = list.size();
int number = size/i;
int remain = size % i;
if(remain != 0){
number++;
}
for(int j=0; j < number; j++){
int start = j * i;
int end = start+ i;
if(end > list.size()){
end = list.size();
}
out.add(list.subList(start, end));
}
return out;
}
Upvotes: 0
Reputation: 10714
A solution using streams, partitioned using a toggle/flip boolean for each item:
Collection<String> big = ...;
AtomicBoolean switcheroo = new AtomicBoolean();
Map<Boolean, List<String>> subLists = big.stream()
.collect(Collectors.partitioningBy(o -> switcheroo.getAndSet(!switcheroo.get())));
You end up with a map with two entries, keys are true
and false
and values are the sub-lists.
This doesn't quite answer the original question, but you could use an AtomicInteger
which increments each time, instead of an AtomicBoolean
.
Upvotes: 0
Reputation: 497
//Here is my list
ArrayList<T> items=new ArrayList<T>();
Integer startIndex = 0;
int j = 0;
for (; j < items.size() - 1; j++) {//always start with next index not again 0
for (int i = 0; i < 4; i++) { // divide into equal part with 4 item
startIndex = startIndex + 1;
j = startIndex;
}
}
Upvotes: 0
Reputation: 391
I use the "Apache Commons Collections 4" library. It has a partition method in the ListUtils class:
...
int targetSize = 100;
List<Integer> largeList = ...
List<List<Integer>> output = ListUtils.partition(largeList, targetSize);
This method is adapted from http://code.google.com/p/guava-libraries/
Upvotes: 3
Reputation: 161
My solution:
List<X> listToSplit = new ArrayList<X>();
List<X> list1 = new ArrayList<X>();
List<X> list2 = new ArrayList<X>();
for (X entry : listToSplit)
{
if (list1.size () > list2.size ())
list2.add (entry);
else
list1.add( entry );
}
Should work :)
Upvotes: 0
Reputation: 64026
Quick semi-pseudo code:
List sub=one.subList(...);
List two=new XxxList(sub);
sub.clear(); // since sub is backed by one, this removes all sub-list items from one
That uses standard List implementation methods and avoids all the running around in loops. The clear() method is also going to use the internal removeRange()
for most lists and be much more efficient.
Upvotes: 57
Reputation: 31
import java.util.Collection;
public class CollectionUtils {
/**
* Will split the passed collection so that the size of the new collections
* is not greater than maxSize
* @param t
* @param maxSize
* @return a List containing splitted collections
*/
@SuppressWarnings("unchecked")
public static <T> List<Collection<T>>split(Collection<T> t, int maxSize) {
int counter = 0;
List<Collection<T>> ret = new LinkedList<Collection<T>>();
Iterator<T>itr = t.iterator();
try {
Collection<T> tmp = t.getClass().newInstance();
ret.add(tmp);
while(itr.hasNext()) {
tmp.add(itr.next());
counter++;
if(counter>=maxSize && itr.hasNext()) {
tmp = t.getClass().newInstance();
ret.add(tmp);
counter=0;
}
}
} catch(Throwable e) {
Logger.getLogger(CollectionUtils.class).error("There was an error spliting "+t.getClass(),e);
}
return ret;
}
}
// JUnit test cases
import java.util.ArrayList;
/**
*
* $Change$
* @version $Revision$
* Last modified date & time $DateTime$
*/
public class CollectionUtilsTest {
@Test
public void testSplitList() {
List<Integer>test = new ArrayList<Integer>(100);
for (int i=1;i<101;i++) {
test.add(i);
}
List<Collection<Integer>> tests = CollectionUtils.split(test, 10);
Assert.assertEquals("Size mismatch", 10,tests.size());
TreeSet<Integer> tmp = new TreeSet<Integer>();
for(Collection<Integer> cs:tests) {
for(Integer i:cs) {
Assert.assertFalse("Duplicated item found "+i,tmp.contains(i));
tmp.add(i);
}
System.out.println(cs);
}
int why = 1;
for(Integer i:tmp) {
Assert.assertEquals("Not all items are in the collection ",why,i.intValue());
why++;
}
}
@Test
public void testSplitSet() {
TreeSet<Integer>test = new TreeSet<Integer>();
for (int i=1;i<112;i++) {
test.add(i);
}
List<Collection<Integer>> tests = CollectionUtils.split(test, 10);
Assert.assertEquals("Size mismatch", 12,tests.size());
TreeSet<Integer> tmp = new TreeSet<Integer>();
int cI = 0;
for(Collection<Integer> cs:tests) {
for(Integer i:cs) {
Assert.assertFalse("Duplicated item found "+i,tmp.contains(i));
tmp.add(i);
}
// if(cI>10) {
System.out.println(cs);
// }
cI++;
}
int why = 1;
for(Integer i:tmp) {
Assert.assertEquals("Not all items are in the collection ",why,i.intValue());
why++;
}
}
}
Upvotes: 3
Reputation: 2735
You can use common utilities, like Guava library:
import com.google.common.collect.Lists;
import com.google.common.math.IntMath;
import java.math.RoundingMode;
int partitionSize = IntMath.divide(list.size(), 2, RoundingMode.UP);
List<List<T>> partitions = Lists.partition(list, partitionSize);
The result is a list of two lists - not quite by your spec, but you can easily adapt, if needed.
Upvotes: 33
Reputation: 2195
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;
}
}
Upvotes: 4
Reputation: 11
A generic function to split a list to a list of list of specific size. I was missing this for long in java collections.
private List<List<T>> splitList(List<T> list, int maxListSize) {
List<List<T>> splittedList = new ArrayList<List<T>>();
int itemsRemaining = list.size();
int start = 0;
while (itemsRemaining != 0) {
int end = itemsRemaining >= maxListSize ? (start + maxListSize) : itemsRemaining;
splittedList.add(list.subList(start, end));
int sizeOfFinalList = end - start;
itemsRemaining = itemsRemaining - sizeOfFinalList;
start = start + sizeOfFinalList;
}
return splittedList;
}
Upvotes: 1
Reputation: 308001
<T> List<T> split(List<T> list, int i) {
List<T> secondPart = list.sublist(i, list.size());
List<T> returnValue = new ArrayList<T>(secondPart());
secondPart.clear(),
return returnValue;
}
Upvotes: 1
Reputation: 4870
Riffing on Marc's solution, this solution uses a for
loop that saves some calls to list.size()
:
<T> List<T> split(List<T> list, int i) {
List<T> x = new ArrayList<T>(list.subList(i, list.size()));
// Remove items from end of original list
for (int j=list.size()-1; j>i; --j)
list.remove(j);
return x;
}
Upvotes: 5
Reputation: 2066
Likewise cribbing off of Marc's list, we'll use List.removeAll() to remove the duplicate entries from the second list. Note that, strictly speaking, this only follows the specs if the original list contained no duplicate items: otherwise, the original list may be missing items.
<T> List<T> split(List<T> list, int i) {
List<T> x = new ArrayList<T>(list.subList(i, list.size()));
// Remove items from end of original list
list.removeAll(x);
return x;
}
Upvotes: 0
Reputation: 45398
Getting the returned array is pretty easy using the subList method, but there's no easy way that I know of to remove a range of items from a List.
Here's what I have:
<T> List<T> split(List<T> list, int i) {
List<T> x = new ArrayList<T>(list.subList(i, list.size()));
// Remove items from end of original list
while (list.size() > i) {
list.remove(list.size() - 1);
}
return x;
}
Upvotes: 4