Java: разделите список на два под-списка?

Какой самый простой, самый стандартный и / или наиболее эффективный способ разбить список на два под-списка на Java? Все в порядке, чтобы изменить исходный список, поэтому копирование не требуется. Подпись метода может быть

/** 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. */  List split(List list, int i); 

[EDIT] List.subList возвращает представление в исходном списке, которое становится недействительным, если оригинал изменен. Таким образом, split не может использовать subList если он также не обходится с исходной ссылкой (или, как в ответе Марка Новаковского, использует subList но сразу же копирует результат).

Быстрый полу-псевдокод:

 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 

Это использует стандартные методы реализации списка и избегает всех запусков в циклах. Метод clear () также будет использовать внутренний removeRange() для большинства списков и будет намного более эффективным.

Вы можете использовать общие утилиты, такие как библиотека 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> partitions = Lists.partition(list, partitionSize); 

Результатом является список из двух списков – не совсем по вашей спецификации, но при необходимости вы можете легко адаптироваться.

Riffing на решении Marc , это решение использует цикл for который сохраняет некоторые вызовы list.size() :

  List split(List list, int i) { List x = new ArrayList(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; } 

Получение возвращаемого массива довольно просто с использованием метода subList, но нет простого способа, которым я знаю, чтобы удалить ряд элементов из списка.

Вот что я имею:

  List split(List list, int i) { List x = new ArrayList(list.subList(i, list.size())); // Remove items from end of original list while (list.size() > i) { list.remove(list.size() - 1); } return x; } 

Мне нужно было что-то подобное, так что это моя реализация. Он позволяет вызывающему указать, какая реализация 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  List> split(final List original, final int maxListSize, final Class 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> lists = new ArrayList>(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  List> split(final List original, final int maxListSize) { return split(original, maxListSize, ArrayList.class); } private static  List createSublist(final T[] elements, final Class listImplementation) { List sublist; final List 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> sublists = ListUtils.split(getPopulatedList(10), 2, LinkedList.class); assertEquals(5, sublists.size()); for (final List sublist : sublists) { assertEquals(2, sublist.size()); assertTrue(sublist instanceof LinkedList); } } @Test public void unevenSplitTest() { final List> 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> sublists = ListUtils.split(getPopulatedList(10), 20, LinkedList.class); assertEquals(1, sublists.size()); assertEquals(10, sublists.get(0).size()); } @Test public void emptyListSplitTest() { final List> 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> sublists = ListUtils.split(getPopulatedList(10), 2, LinkedList.class); assertEquals(5, sublists.size()); for (final List sublist : sublists) { assertEquals(2, sublist.size()); assertTrue(sublist instanceof LinkedList); } } private List getPopulatedList(final int size) { final List list = new ArrayList(10); for (int i = 0; i < 10; i++) { list.add(new Object()); } return list; } 

}

 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  List>split(Collection t, int maxSize) { int counter = 0; List> ret = new LinkedList>(); Iteratoritr = t.iterator(); try { Collection 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() { Listtest = new ArrayList(100); for (int i=1;i<101;i++) { test.add(i); } List> tests = CollectionUtils.split(test, 10); Assert.assertEquals("Size mismatch", 10,tests.size()); TreeSet tmp = new TreeSet(); for(Collection 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() { TreeSettest = new TreeSet(); for (int i=1;i<112;i++) { test.add(i); } List> tests = CollectionUtils.split(test, 10); Assert.assertEquals("Size mismatch", 12,tests.size()); TreeSet tmp = new TreeSet(); int cI = 0; for(Collection 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++; } } } 

Я использую библиотеку « Apache Commons Collections 4 ». Он имеет метод разбиения в classе ListUtils:

 ... int targetSize = 100; List largeList = ... List> output = ListUtils.partition(largeList, targetSize); 

Этот метод адаптирован с http://code.google.com/p/guava-libraries/

  List split(List list, int i) { List secondPart = list.sublist(i, list.size()); List returnValue = new ArrayList(secondPart()); secondPart.clear(), return returnValue; } 

Общая функция для разбиения списка на список списка определенного размера. Я долго отсутствовал в коллекциях java.

 private List> splitList(List list, int maxListSize) { List> splittedList = new ArrayList>(); 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; } 

Аналогично, вырезая список Marc, мы будем использовать List.removeAll (), чтобы удалить дубликаты записей из второго списка. Обратите внимание, что это, строго говоря, следует только спецификациям, если в исходном списке не было повторяющихся элементов: в противном случае в исходном списке могут отсутствовать элементы.

  List split(List list, int i) { List x = new ArrayList(list.subList(i, list.size())); // Remove items from end of original list list.removeAll(x); return x; } 

образец java-кода для разделения списка

public List> split (Список списка, int i) {

  List> out = new ArrayList>(); 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; } 

Мое решение:

 List listToSplit = new ArrayList(); List list1 = new ArrayList(); List list2 = new ArrayList(); for (X entry : listToSplit) { if (list1.size () > list2.size ()) list2.add (entry); else list1.add( entry ); } 

Должно сработать 🙂