Chris Conway
Chris Conway

Reputation: 55989

Java: split a List into two sub-Lists?

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

Answers (15)

Grant
Grant

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

kem
kem

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

Sam Barnum
Sam Barnum

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

Avinash Khadsan
Avinash Khadsan

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

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

Xesau
Xesau

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

L. Cornelius Dol
L. Cornelius Dol

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

NaAl
NaAl

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

altumano
altumano

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

Mat&#237;as R
Mat&#237;as R

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

Coder by Force
Coder by Force

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

Joachim Sauer
Joachim Sauer

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

Brandon DuRette
Brandon DuRette

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

James
James

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

Marc Novakowski
Marc Novakowski

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

Related Questions