web-dev-qa-db-fra.com

Existe-t-il un utilitaire Java commun permettant de fractionner une liste en lots?

Je me suis écrit un utilitaire pour diviser une liste en lots de taille donnée. Je voulais juste savoir s'il existe déjà des utilitaires Apache communs pour cela.

public static <T> List<List<T>> getBatches(List<T> collection,int batchSize){
    int i = 0;
    List<List<T>> batches = new ArrayList<List<T>>();
    while(i<collection.size()){
        int nextInc = Math.min(collection.size()-i,batchSize);
        List<T> batch = collection.subList(i,i+nextInc);
        batches.add(batch);
        i = i + nextInc;
    }

    return batches;
}

S'il vous plaît laissez-moi savoir s'il y a un utilitaire existant déjà pour le même.

97
Harish

Découvrez Lists.partition(Java.util.List, int) from Google Guava :

Renvoie des sous-listes consécutives d'une liste, chacune de la même taille (la liste finale peut être plus petite). Par exemple, le fait de partitionner une liste contenant [a, b, c, d, e] avec une taille de partition de 3 donne [[a, b, c], [d, e]] - une liste externe contenant deux listes internes de trois et deux éléments, le tout dans l'ordre d'origine.

203
Tomasz Nurkiewicz

Si vous souhaitez générer un flux de lots Java-8, vous pouvez essayer le code suivant:

public static <T> Stream<List<T>> batches(List<T> source, int length) {
    if (length <= 0)
        throw new IllegalArgumentException("length = " + length);
    int size = source.size();
    if (size <= 0)
        return Stream.empty();
    int fullChunks = (size - 1) / length;
    return IntStream.range(0, fullChunks + 1).mapToObj(
        n -> source.subList(n * length, n == fullChunks ? size : (n + 1) * length));
}

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);

    System.out.println("By 3:");
    batches(list, 3).forEach(System.out::println);

    System.out.println("By 4:");
    batches(list, 4).forEach(System.out::println);
}

Sortie:

By 3:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10, 11, 12]
[13, 14]
By 4:
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10, 11, 12]
[13, 14]
38
Tagir Valeev

Une autre approche consiste à utiliser Collectors.groupingBy of index, puis à mapper les index groupés aux éléments réels: 

    final List<Integer> numbers = range(1, 12)
            .boxed()
            .collect(toList());
    System.out.println(numbers);

    final List<List<Integer>> groups = range(0, numbers.size())
            .boxed()
            .collect(groupingBy(index -> index / 4))
            .values()
            .stream()
            .map(indices -> indices
                    .stream()
                    .map(numbers::get)
                    .collect(toList()))
            .collect(toList());
    System.out.println(groups);

Sortie:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]

11
Adrian Bona

Je suis venu avec celui-ci:

private static <T> List<List<T>> partition(Collection<T> members, int maxSize)
{
    List<List<T>> res = new ArrayList<>();

    List<T> internal = new ArrayList<>();

    for (T member : members)
    {
        internal.add(member);

        if (internal.size() == maxSize)
        {
            res.add(internal);
            internal = new ArrayList<>();
        }
    }
    if (internal.isEmpty() == false)
    {
        res.add(internal);
    }
    return res;
}
5
Raz Coren

L'exemple suivant illustre le découpage d'une liste:

package de.thomasdarimont.labs;

import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;

public class SplitIntoChunks {

    public static void main(String[] args) {

        List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);

        List<List<Integer>> chunks = chunk(ints, 4);

        System.out.printf("Ints:   %s%n", ints);
        System.out.printf("Chunks: %s%n", chunks);
    }

    public static <T> List<List<T>> chunk(List<T> input, int chunkSize) {

        int inputSize = input.size();
        int chunkCount = (int) Math.ceil(inputSize / (double) chunkSize);

        Map<Integer, List<T>> map = new HashMap<>(chunkCount);
        List<List<T>> chunks = new ArrayList<>(chunkCount);

        for (int i = 0; i < inputSize; i++) {

            map.computeIfAbsent(i / chunkSize, (ignore) -> {

                List<T> chunk = new ArrayList<>();
                chunks.add(chunk);
                return chunk;

            }).add(input.get(i));
        }

        return chunks;
    }
}

Sortie:

Ints:   [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Chunks: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]
4
Thomas Darimont

Avec Java 9, vous pouvez utiliser IntStream.iterate() avec la condition hasNext. Donc, vous pouvez simplifier le code de votre méthode à ceci:

public static <T> List<List<T>> getBatches(List<T> collection, int batchSize) {
    return IntStream.iterate(0, i -> i < collection.size(), i -> i + batchSize)
            .mapToObj(i -> collection.subList(i, Math.min(i + batchSize, collection.size())))
            .collect(Collectors.toList());
}

En utilisant {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, le résultat de getBatches(numbers, 4) sera:

[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]]
3
Samuel Philipp

En utilisant diverses astuces du Web, je suis arrivé à cette solution:

int[] count = new int[1];
final int CHUNK_SIZE = 500;
Map<Integer, List<Long>> chunkedUsers = users.stream().collect( Collectors.groupingBy( 
    user -> {
        count[0]++;
        return Math.floorDiv( count[0], CHUNK_SIZE );
    } )
);

Nous utilisons count pour imiter un index de collection normal.
Ensuite, nous regroupons les éléments de la collection dans des compartiments, en utilisant le quotient algébrique comme numéro de compartiment.
La carte finale contient sous la forme clé le numéro du compartiment, sous forme de valeur le compartiment lui-même.

Vous pouvez ensuite facilement effectuer une opération sur chacun des seaux avec:

chunkedUsers.values().forEach( ... );
3
Nicolas Nobelis

Il y avait une autre question qui était fermée comme étant une copie de celle-ci, mais si vous la lisez attentivement, elle est légèrement différente. Donc, au cas où quelqu'un (comme moi) souhaite scinder une liste en un nombre donné de sous-listes de taille presque égale, puis continuez votre lecture.

J'ai simplement porté l'algorithme décrit ici en Java.

@Test
public void shouldPartitionListIntoAlmostEquallySizedSublists() {

    List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f", "g");
    int numberOfPartitions = 3;

    List<List<String>> split = IntStream.range(0, numberOfPartitions).boxed()
            .map(i -> list.subList(
                    partitionOffset(list.size(), numberOfPartitions, i),
                    partitionOffset(list.size(), numberOfPartitions, i + 1)))
            .collect(toList());

    assertThat(split, hasSize(numberOfPartitions));
    assertEquals(list.size(), split.stream().flatMap(Collection::stream).count());
    assertThat(split, hasItems(Arrays.asList("a", "b", "c"), Arrays.asList("d", "e"), Arrays.asList("f", "g")));
}

private static int partitionOffset(int length, int numberOfPartitions, int partitionIndex) {
    return partitionIndex * (length / numberOfPartitions) + Math.min(partitionIndex, length % numberOfPartitions);
}
1
Stefan Reisner

Utilisez Apache Commons ListUtils.partition .

1
Paul Rambags
List<T> batch = collection.subList(i,i+nextInc);
->
List<T> batch = collection.subList(i, i = i + nextInc);
1
Yohann

Une autre approche pour résoudre ce problème, la question:

public class CollectionUtils {

    /**
    * Splits the collection into lists with given batch size
    * @param collection to split in to batches
    * @param batchsize size of the batch
    * @param <T> it maintains the input type to output type
    * @return nested list
    */
    public static <T> List<List<T>> makeBatch(Collection<T> collection, int batchsize) {

        List<List<T>> totalArrayList = new ArrayList<>();
        List<T> tempItems = new ArrayList<>();

        Iterator<T> iterator = collection.iterator();

        for (int i = 0; i < collection.size(); i++) {
            tempItems.add(iterator.next());
            if ((i+1) % batchsize == 0) {
                totalArrayList.add(tempItems);
                tempItems = new ArrayList<>();
            }
        }

        if (tempItems.size() > 0) {
            totalArrayList.add(tempItems);
        }

        return totalArrayList;
    }

}
0
Jurrian Fahner

import com.google.common.collect.Lists;

List<List<T>> batches = Lists.partition(List<T>,batchSize)

Utilisez Lists.partition (List, batchSize). Vous devez importer Lists à partir de Google Common Package (com.google.common.collect.Lists)

Il retournera une liste de List<T> avec et la taille de chaque élément égale à votre batchSize.

0
v87278

Un one-line dans Java 8 serait:

import static Java.util.function.Function.identity;
import static Java.util.stream.Collectors.*;

private static <T> Collection<List<T>> partition(List<T> xs, int size) {
    return IntStream.range(0, xs.size())
            .boxed()
            .collect(collectingAndThen(toMap(identity(), xs::get), Map::entrySet))
            .stream()
            .collect(groupingBy(x -> x.getKey() / size, mapping(Map.Entry::getValue, toList())))
            .values();

}
0
Ori Popowski