Java: diviser une liste en deux sous-listes?
Quel est le moyen le plus simple, le plus standard et/ou le plus efficace de diviser une liste en deux sous-listes en Java? Il est possible de muter la liste originale, donc aucune copie ne devrait être nécessaire. La signature de la méthode pourrait être
/** 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
retourne une vue sur la liste originale, qui devient invalide si l'original est modifié. Ainsi split
ne peut pas utiliser subList
à moins qu'il ne dispense également de la référence originale (ou, comme dans Marc Novakowski réponse, utilise subList
mais copie immédiatement le résultat).
12 réponses
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
qui utilise des méthodes de mise en œuvre de listes standard et évite toute rotation en boucle. La méthode clear() va également utiliser le removeRange()
interne pour la plupart des listes et être beaucoup plus efficace.
vous pouvez utiliser des utilitaires courants, comme bibliothèque 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);
le résultat est une liste de deux listes - pas tout à fait selon vos spécifications, mais vous pouvez facilement vous adapter, si nécessaire.
Riffing on Marc's solution , cette solution utilise une boucle for
qui sauve quelques appels à 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;
}
obtenir le tableau retourné est assez facile en utilisant la méthode de sous-liste, mais il n'y a pas de moyen facile que je connaisse pour supprimer une gamme d'éléments d'une liste.
voilà ce que j'ai:
<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;
}
j'avais besoin de quelque chose de similaire donc c'est mon implémentation. Il permet à l'appelant de préciser quelle implémentation de la liste doit être retournée:
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;
}
}
et certains cas d'essai:
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;
}
}
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++;
}
}
}
j'utilise le " Apache Commons Collections 4 " de la bibliothèque. Il a une méthode de partition dans la classe ListUtils:
...
int targetSize = 100;
List<Integer> largeList = ...
List<List<Integer>> output = ListUtils.partition(largeList, targetSize);
cette méthode est adaptée de http://code.google.com/p/guava-libraries /
<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;
}
une fonction générique pour diviser une liste en une liste de taille spécifique. Ça m'a longtemps manqué dans les collections 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;
}
de la même façon que pour la liste de Marc, nous utiliserons la liste.removeAll () pour supprimer les entrées en double de la deuxième liste. Notez que, à strictement parler, cela ne suit les spécifications que si la liste originale ne contenait pas de doublons: sinon, la liste originale peut être manquante.
<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;
}
exemple de code java pour diviser la liste
Liste publique> split(Liste de liste, 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;
}
ma 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 );
}
devrait fonctionner:)