Apakah ada utilitas Java umum untuk memecah daftar menjadi beberapa batch?

140

Saya menulis sendiri sebuah utilitas untuk memecah daftar menjadi beberapa ukuran. Saya hanya ingin tahu apakah sudah ada util apache commons untuk ini.

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

Tolong beri tahu saya jika sudah ada utilitas yang sama.

Harish
sumber
4
Tidak yakin ini di luar topik. Pertanyaannya bukan "perpustakaan apa yang melakukan ini" tetapi "bagaimana saya bisa melakukan ini dengan utilitas umum apache".
Florian F
@FlorianF Saya setuju dengan Anda. Pertanyaan ini dan jawabannya sangat berguna, dan dapat disimpan dengan baik dengan sedikit suntingan. Tindakan malas untuk menutupnya dengan tergesa-gesa.
Endery
Ditemukan posting blog yang bermanfaat dengan kelas dan tolok ukur yang bagus di sini: e.printstacktrace.blog/…
Benj

Jawaban:

249

Lihat dari Google Guava : Lists.partition(java.util.List, int)

Mengembalikan sublists berturut-turut dari daftar, masing-masing dengan ukuran yang sama (daftar akhir mungkin lebih kecil). Misalnya, mempartisi daftar yang berisi [a, b, c, d, e]dengan ukuran partisi 3 hasil [[a, b, c], [d, e]]- daftar luar yang berisi dua daftar bagian dalam tiga dan dua elemen, semua dalam urutan asli.

Tomasz Nurkiewicz
sumber
tautan partition documentation dan tautan code example
Austin Haws
16
Untuk pengguna umum apache, fungsi ini juga tersedia: commons.apache.org/proper/commons-collections/apidocs/org/…
Xavier Portebois
3
f Anda bekerja dengan daftar saya menggunakan perpustakaan "Apache Commons Collections 4". Ini memiliki metode partisi di kelas ListUtils: ... int targetSize = 100; Daftar <Integer> largeList = ... Daftar <Daftar <Integer>> output = ListUtils.partition (largeList, targetSize); Metode ini diadaptasi dari code.google.com/p/guava-libraries
Swapnil Jaju
1
Terima kasih. Saya tidak percaya betapa sulitnya melakukan ini di Jawa.
Rambut Panjang Paman
51

Jika Anda ingin menghasilkan aliran batch Java-8, Anda dapat mencoba kode berikut:

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

Keluaran:

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
sumber
Bagaimana cara saya mematahkan, melanjutkan, atau kembali dalam pendekatan ini?
Miral
15

Pendekatan lain adalah dengan menggunakan Collectors.groupingByindeks dan kemudian memetakan indeks yang dikelompokkan ke elemen aktual:

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

Keluaran:

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

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

Adrian Bona
sumber
1
@Sebien Ini berfungsi untuk kasus umum. Ini groupingBydilakukan pada elemen IntStream.range, bukan elemen daftar. Lihat misalnya ideone.com/KYBc7h .
Radiodef
@MohammedElrashidy Sebien telah menghapus komentar mereka, kini Anda dapat menghapus komentar Anda.
Albert Hendriks
7

Saya datang dengan yang ini:

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
sumber
6

Dengan Java 9 Anda dapat menggunakan IntStream.iterate()dengan hasNextkondisi. Jadi, Anda dapat menyederhanakan kode metode Anda untuk ini:

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

Menggunakan {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, hasil dari getBatches(numbers, 4)akan:

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

Contoh berikut menunjukkan pemotongan Daftar:

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

Keluaran:

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
sumber
4

Ada pertanyaan lain yang ditutup sebagai duplikat dari pertanyaan ini, tetapi jika Anda membacanya dengan cermat, itu agak berbeda. Jadi kalau-kalau seseorang (seperti saya) benar-benar ingin membagi daftar menjadi sejumlah daftar yang berukuran hampir sama , kemudian baca terus.

Saya hanya porting algoritma yang dijelaskan di sini ke Jawa.

@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);
}
Stefan Reisner
sumber
4

Gunakan Apache Commons ListUtils.partition .

org.apache.commons.collections4.ListUtils.partition(final List<T> list, final int size)
Paul Rambags
sumber
3

Menggunakan berbagai cheat dari web, saya sampai pada solusi ini:

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

Kami menggunakan hitungan untuk meniru indeks pengumpulan normal.
Kemudian, kami mengelompokkan elemen koleksi dalam ember, menggunakan hasil bagi aljabar sebagai nomor ember.
Peta akhir berisi sebagai kunci nomor ember, sebagai nilai ember itu sendiri.

Anda kemudian dapat dengan mudah melakukan operasi pada masing-masing ember dengan:

chunkedUsers.values().forEach( ... );
Nicolas Nobelis
sumber
4
Bisa menggunakan AtomicIntegerhitungan for.
jkschneider
1
List<T> batch = collection.subList(i,i+nextInc);
->
List<T> batch = collection.subList(i, i = i + nextInc);
Yohann
sumber
1

Mirip dengan OP tanpa aliran dan lib, tetapi lebih sadar:

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
sumber
0

Pendekatan lain untuk mengatasi ini, pertanyaan:

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
sumber
0

Satu kalimat di Java 8 adalah:

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
sumber
0

Berikut ini adalah solusi sederhana untuk 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
sumber
0

Anda dapat menggunakan kode di bawah ini untuk mendapatkan kumpulan daftar.

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

Anda perlu mengimpor perpustakaan Google Guava untuk menggunakan kode di atas.

mukul28.03
sumber
-1

import com.google.common.collect.Lists;

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

Gunakan Lists.partition (List, batchSize). Anda perlu mengimpor Listsdari paket umum google ( com.google.common.collect.Lists)

Ini akan mengembalikan Daftar List<T>dengan dan ukuran setiap elemen sama dengan Anda batchSize.

v87278
sumber
Anda juga dapat menggunakan subList(startIndex, endIndex)metode mereka sendiri untuk memecahkan daftar berdasarkan indeks yang diperlukan.
v87278