Czy istnieje popularne narzędzie Java do dzielenia listy na partie?

141

Napisałem sobie narzędzie do dzielenia listy na partie o określonej wielkości. Chciałem tylko wiedzieć, czy jest już do tego celu użyta jakaś wspólna apache.

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

Daj mi znać, jeśli istnieje już jakieś narzędzie dla tego samego.

Harish
źródło
4
Nie jestem pewien, czy to nie na temat. Pytanie nie brzmi „jaka biblioteka to robi”, ale „jak mogę to zrobić za pomocą popularnych narzędzi Apache”.
Florian F
@FlorianF Zgadzam się z tobą. To pytanie i odpowiedzi na nie są bardzo przydatne i można je dobrze zapisać, wprowadzając niewielką edycję. Zamknięcie go w pośpiechu było leniwym działaniem.
Endery
Znalazłem przydatny post na blogu z fajną klasą i testami
Benj

Odpowiedzi:

250

Sprawdź z Google Guava : Lists.partition(java.util.List, int)

Zwraca kolejne podlisty listy, każda o tej samej wielkości (ostateczna lista może być mniejsza). Na przykład partycjonowanie listy zawierającej [a, b, c, d, e]partycje o rozmiarze 3 daje [[a, b, c], [d, e]]- lista zewnętrzna zawierająca dwie wewnętrzne listy składające się z trzech i dwóch elementów, wszystkie w oryginalnej kolejności.

Tomasz Nurkiewicz
źródło
link partition documentation i link code example
Austin Haws
16
Dla zwykłych użytkowników apache ta funkcja jest również dostępna: commons.apache.org/proper/commons-collections/apidocs/org/…
Xavier Portebois
3
Jeśli pracujesz z listą, używam biblioteki „Apache Commons Collections 4”. Ma metodę partycji w klasie ListUtils: ... int targetSize = 100; List <Integer> largeList = ... List <List <Integer>> output = ListUtils.partition (largeList, targetSize); Ta metoda została zaadaptowana z code.google.com/p/guava-libraries
Swapnil Jaju,
1
Dziękuję Ci. Nie mogę uwierzyć, jak trudno to zrobić w Javie.
Uncle Long Hair
51

Jeśli chcesz utworzyć strumień partii Java-8, możesz wypróbować następujący kod:

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

Wynik:

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]
Tagir Valeev
źródło
Jak mogę przerwać, kontynuować lub powrócić w tym podejściu?
Miral
15

Innym podejściem jest użycie Collectors.groupingByindeksów, a następnie odwzorowanie zgrupowanych indeksów na rzeczywiste elementy:

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

Wynik:

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

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

Adrian Bona
źródło
1
@Sebien To działa w ogólnym przypadku. groupingByOdbywa się pierwiastków z IntStream.range, a nie elementów listy. Zobacz np . Ideone.com/KYBc7h .
Radiodef
@MohammedElrashidy Sebien usunął swój komentarz, teraz możesz usunąć swój.
Albert Hendriks
7

Wymyśliłem ten:

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;
}
Raz Coren
źródło
6

Java 9 można skorzystać IntStream.iterate()z hasNextwarunku. Możesz więc uprościć kod swojej metody do tego:

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

Używając {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, wynikiem getBatches(numbers, 4)będzie:

[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]]
Samuel Philipp
źródło
5

Poniższy przykład ilustruje fragmentację listy:

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

Wynik:

Ints:   [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Chunks: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]
Thomas Darimont
źródło
4

Było jeszcze jedno pytanie, które zostało zamknięte jako duplikat tego, ale jeśli uważnie je przeczytasz, jest nieco inne. Jeśli więc ktoś (taki jak ja) rzeczywiście chce podzielić listę na określoną liczbę podlist o prawie równej wielkości , czytaj dalej.

Po prostu przeportowałem opisany tutaj algorytm na Javę.

@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);
}
Stefana Reisnera
źródło
4

Użyj Apache Commons ListUtils.partition .

org.apache.commons.collections4.ListUtils.partition(final List<T> list, final int size)
Paul Rambags
źródło
3

Korzystając z różnych kodów z sieci, doszedłem do takiego rozwiązania:

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

Używamy count do naśladowania normalnego indeksu kolekcji.
Następnie grupujemy elementy kolekcji w segmenty, używając ilorazu algebraicznego jako numeru segmentu.
Ostateczna mapa zawiera jako klucz numer segmentu jako wartość samego segmentu.

Następnie możesz łatwo wykonać operację na każdym z wiader za pomocą:

chunkedUsers.values().forEach( ... );
Nicolas Nobelis
źródło
4
Przydałoby się AtomicIntegerliczyć.
jkschneider
1
List<T> batch = collection.subList(i,i+nextInc);
->
List<T> batch = collection.subList(i, i = i + nextInc);
Yohann
źródło
1

Podobny do OP bez strumieni i bibliotek, ale bardziej zwięzły:

public <T> List<List<T>> getBatches(List<T> collection, int batchSize) {
    List<List<T>> batches = new ArrayList<>();
    for (int i = 0; i < collection.size(); i += batchSize) {
        batches.add(collection.subList(i, Math.min(i + batchSize, collection.size())));
    }
    return batches;
}
Albert Hendriks
źródło
0

Inne podejście do rozwiązania tego problemu, pytanie:

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

}
Jurrian Fahner
źródło
0

Jednowierszowy w Javie 8 byłby:

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();

}
Ori Popowski
źródło
0

Oto proste rozwiązanie dla Java 8+:

public static <T> Collection<List<T>> prepareChunks(List<T> inputList, int chunkSize) {
    AtomicInteger counter = new AtomicInteger();
    return inputList.stream().collect(Collectors.groupingBy(it -> counter.getAndIncrement() / chunkSize)).values();
}
aleastD
źródło
0

Możesz użyć poniższego kodu, aby uzyskać partię listy.

Iterable<List<T>> batchIds = Iterables.partition(list, batchSize);

Aby użyć powyższego kodu, musisz zaimportować bibliotekę Google Guava.

mukul28.03
źródło
-1

import com.google.common.collect.Lists;

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

Użyj Lists.partition (List, batchSize). Musisz zaimportować Listsze wspólnego pakietu Google ( com.google.common.collect.Lists)

Zwróci List of List<T>with i rozmiar każdego elementu równy twojemu batchSize.

v87278
źródło
Możesz również użyć własnej subList(startIndex, endIndex)metody podziału listy na podstawie wymaganego indeksu.
v87278