มียูทิลิตี Java ทั่วไปเพื่อแบ่งรายการเป็นแบตช์หรือไม่?


141

ฉันเขียนโปรแกรมอรรถประโยชน์เพื่อแบ่งรายการเป็นชุดขนาดที่กำหนด ฉันแค่อยากจะรู้ว่ามี 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;
}

โปรดแจ้งให้เราทราบหากมียูทิลิตี้ที่มีอยู่แล้วสำหรับสิ่งเดียวกัน


4
ไม่แน่ใจว่านี่เป็นนอกหัวข้อ คำถามไม่ใช่ "ห้องสมุดนี้ทำสิ่งใด" แต่ "ฉันจะทำสิ่งนี้ได้อย่างไรด้วยเครื่องมือทั่วไปของ Apache"
Florian F

@ FlorianF ฉันเห็นด้วยกับคุณ คำถามและคำตอบนี้มีประโยชน์มากและสามารถบันทึกได้ดีด้วยการแก้ไขเล็กน้อย มันเป็นการกระทำที่ขี้เกียจที่จะปิดมันอย่างเร่งรีบ
Endery

พบโพสต์บล็อกที่มีประโยชน์พร้อมชั้นเรียนที่ดีและมาตรฐานที่นี่: e.printstacktrace.blog/…
Benj

คำตอบ:


250

ลองดูจากGoogle Guava : Lists.partition(java.util.List, int)

ส่งคืนรายการย่อยต่อเนื่องของรายการแต่ละรายการมีขนาดเท่ากัน (รายการสุดท้ายอาจมีขนาดเล็กกว่า) ยกตัวอย่างเช่นการแยกรายการที่มี[a, b, c, d, e]ขนาดพาร์ติชัน 3 อัตราผลตอบแทน[[a, b, c], [d, e]]- รายการนอกที่มีรายชื่อสองชั้นสามและสององค์ประกอบทั้งหมดในการสั่งซื้อเดิม


ลิงค์ partition documentationและ ลิงค์ code example
Austin Haws

16
สำหรับผู้ใช้ทั่วไปของ apache, ฟังก์ชั่นนี้ยังมีอยู่: commons.apache.org/proper/commons-collections/apidocs/org/…
Xavier Portebois

3
ถ้าคุณทำงานกับรายการฉันใช้ไลบรารี "Apache Commons Collections 4" มันมีวิธีการแบ่งพาร์ติชันในคลาส ListUtils: ... int targetSize = 100; List <Integer> largeList = ... List <List <Integer>> output = ListUtils.partition (largeList, targetSize); วิธีนี้ถูกดัดแปลงจากcode.google.com/p/guava-l
ไลบรารี

1
ขอบคุณ. ฉันไม่อยากจะเชื่อเลยว่าจะต้องทำอย่างไรใน Java
ลุงผมยาว

51

ในกรณีที่คุณต้องการสร้างกระแสข้อมูลจำนวนมากของ Java-8 คุณสามารถลองใช้รหัสต่อไปนี้:

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

เอาท์พุท:

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]

ฉันจะทำลายทำต่อหรือกลับมาในแนวทางนี้ได้อย่างไร
Miral

15

อีกวิธีหนึ่งคือการใช้Collectors.groupingByดัชนีแล้วแมปดัชนีที่จัดกลุ่มกับองค์ประกอบจริง:

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

เอาท์พุท:

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

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


1
@Sebien วิธีนี้ใช้ได้กับกรณีทั่วไป groupingByจะทำในองค์ประกอบของที่IntStream.rangeไม่องค์ประกอบของรายการ ดูเช่นideone.com/KYBc7h
Radiodef

@MohammedElrashidy Sebien ได้ลบความคิดเห็นออกแล้วคุณสามารถลบความเห็นของคุณออกได้
Albert Hendriks

7

ฉันมากับอันนี้:

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

ด้วย Java 9 คุณสามารถใช้IntStream.iterate()กับhasNextเงื่อนไข ดังนั้นคุณสามารถทำให้โค้ดของวิธีการของคุณง่ายขึ้น:

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

การใช้{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}ผลลัพธ์getBatches(numbers, 4)จะเป็น:

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

5

ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการ chunking ของรายการ:

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

เอาท์พุท:

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

มีอีกคำถามหนึ่งที่ถูกปิดเหมือนเป็นสำเนาของคำถามนี้ แต่ถ้าคุณอ่านอย่างใกล้ชิด ดังนั้นในกรณีที่บางคน (เช่นฉัน) ต้องการแบ่งรายการออกเป็นรายการย่อยที่มีขนาดเกือบเท่ากันแล้วอ่านต่อ

ฉันเพียงแค่ส่งอัลกอริทึมที่อธิบายไว้ที่นี่เพื่อ 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);
}


3

ใช้กลโกงต่าง ๆ จากเว็บฉันมาที่วิธีนี้:

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

เราใช้จำนวนเพื่อเลียนแบบดัชนีการรวบรวมตามปกติ
จากนั้นเราจัดกลุ่มองค์ประกอบการรวบรวมเป็นที่เก็บโดยใช้ความฉลาดทางพีชคณิตเป็นหมายเลขที่ฝากข้อมูล
แม็พสุดท้ายมีคีย์รหัสที่ฝากข้อมูลเป็นค่าที่เก็บข้อมูลเอง

จากนั้นคุณสามารถดำเนินการกับแต่ละถังด้วย:

chunkedUsers.values().forEach( ... );

4
สามารถใช้การAtomicIntegerนับ
jkschneider


1

คล้ายกับ OP โดยไม่มีสตรีมและ libs แต่มีความกระชับ:

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

อีกวิธีหนึ่งในการแก้ปัญหานี้คือคำถาม:

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

หนึ่งซับใน Java 8 จะเป็น:

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

นี่เป็นวิธีง่ายๆสำหรับ 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

คุณสามารถใช้รหัสด้านล่างเพื่อรับชุดของรายการ

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

คุณต้องนำเข้าห้องสมุด Google Guava เพื่อใช้รหัสด้านบน


-1

import com.google.common.collect.Lists;

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

ใช้ Lists.partition (รายการ batchSize) คุณต้องนำเข้าListsจากแพ็คเกจทั่วไปของ google ( com.google.common.collect.Lists)

มันจะกลับรายการของที่มีและขนาดของทุกองค์ประกอบเท่ากับของคุณList<T>batchSize


นอกจากนี้คุณยังสามารถใช้subList(startIndex, endIndex)วิธีการของตนเองสำหรับการแบ่งรายการตามดัชนีที่ต้องการ
v87278
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.