tags:

views:

6456

answers:

8

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).

+4  A: 

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;
}
Marc Novakowski
One clarification - I create a new ArrayList because the list returned by subList is backed by the original list, and since we're modifying it later it would mess up the returned list.
Marc Novakowski
Absolutely right. Thanks for pointing out the bugs in my (now deleted) attempt - that's what I get for posting half-asleep...
Dan Vinton
.subList(i, list.Size()).clear() should remove the items contained in the subList from the base List (as mentioned in the JavaDoc of subList). See my answer below for an example.
Joachim Sauer
+3  A: 

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;
}
Brandon DuRette
+1  A: 
<T> List<T> split(List<T> list, int i) {
    List<T> upper = new ArrayList<T>();
    while (list.size() > i) {
        upper.add(list.remove(i));
    }
    return upper;
}
Dan Vinton
+11  A: 

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 a heckuva lot more efficient.

Software Monkey
+1  A: 

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;
}
James
I think this solution fails in the case where elements that are .equals() appear in both partitions.
Brandon DuRette
+1  A: 
<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;
}
Joachim Sauer
Nice way to clear out the end of the original List without using a for loop, +1
Marc Novakowski
A: 

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;
    }
Coder by Force
A: 

In the last posting from Coder by Force, there is an error, the first line of the while block must be like this:

int end = itemsRemaining >= maxListSize ? (start + maxListSize) : (start + itemsRemaining);

Carsten