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.


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.


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]

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]]


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

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]]

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]]

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


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

4
Bisa menggunakan AtomicIntegerhitungan for.
jkschneider

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

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

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

}

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

}

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

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.


-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.


Anda juga dapat menggunakan subList(startIndex, endIndex)metode mereka sendiri untuk memecahkan daftar berdasarkan indeks yang diperlukan.
v87278
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.