Java:将List拆分为两个子List?

37

在Java中,最简单、最标准、或者最高效的方法是将一个List分成两个子List。原始的List可以进行修改,无需进行复制。方法签名可能如下:

public void split(List<T> original, List<T> firstHalf, List<T> secondHalf);

/** 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);
[编辑] List.subList 返回原始列表的视图,如果修改了原始列表,则该视图将无效。因此,split 不能使用 subList,除非它也放弃了对原始引用的使用(或者,如Marc Novakowski的答案所示,使用 subList,但立即复制结果)。

“最高效”不是取决于List的具体类型吗? - Miserable Variable
你的注释应该以"/**"开头,因为它是一个方法注释。 - Steve Kuo
Hemal,可能是这样。所以请给我最简单和最标准的方法。 - Chris Conway
1
@Hemal:幸运的是,使用subList()可以让每个列表实现最快的操作。 - Joachim Sauer
14个回答

56

简单的半伪代码:

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

这将使用标准的List实现方法,避免所有循环中的运行。clear() 方法也将对大多数列表使用内部的 removeRange() 并更加高效。


list.subList(first, Math.min(list.size(), first + pagesize)); 列表.子列表(第一个, Math.min(列表.大小(), 第一个 + 页面大小)); - Junchen Liu
@shanyangqu:不会,因为子列表是由原始列表支持的,它不会清除子列表中原始列表的项目。基本上,子列表只是对更大列表的视图 - Lawrence Dol

33

您可以使用常用工具,例如Guava库:

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);
结果是两个列表的列表-虽然不完全符合您的规格,但如果需要,您可以轻松地进行适应。

5

参考Marc的解决方案,这个解决方案使用了一个 for 循环来避免多次调用 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;
}

4

我需要类似的东西,所以这是我的实现。它允许调用者指定应返回哪个List实现:

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;
}

并且一些测试用例:

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;
}

}


4
使用subList方法获取返回的数组非常容易,但我不知道有什么简单的方法可以从List中删除一定范围内的项目。以下是我的代码:
<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;
}

一个澄清 - 我创建一个新的ArrayList是因为subList返回的列表与原始列表相关联,而且由于我们稍后要修改它,这会使返回的列表混乱。 - Marc Novakowski
完全正确。感谢您指出我(现已删除)尝试中的错误 - 这就是我半夜发布的结果... - Dan Vinton
.subList(i, list.Size()).clear() 应该从基本列表中删除包含在子列表中的项目(如 subList 的 JavaDoc 中所述)。请参阅下面的答案以获得示例。 - Joachim Sauer

3
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++;
    }
  }
}

3

1
一个通用的函数,将列表分割成特定大小的列表。在Java集合中长期缺少这个功能。
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;
    }

1
你的代码有错误,while块的第一行必须像这样:int end = itemsRemaining >= maxListSize ? (start + maxListSize) : **(start + itemsRemaining)**; - user350089

1
<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;
}

0

Java示例代码分割列表

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;
}

问题要求在索引i处拆分为两个子列表,而不是长度为in个子列表。 - Lawrence Dol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接