200

我给自己写了一个实用程序,可以将列表分成给定大小的批次。我只是想知道是否已经有任何 apache commons util 用于此。

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

19 回答 19

310

Google Guava查看: Lists.partition(java.util.List, int)

返回列表的连续子列表,每个子列表大小相同(最终列表可能更小)。例如,对包含[a, b, c, d, e]3 的分区大小的列表进行分区会产生[[a, b, c], [d, e]]-- 一个包含两个由三个和两个元素组成的内部列表的外部列表,所有这些都按原始顺序排列。

于 2012-08-19T13:38:07.477 回答
91

如果要生成 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]
于 2015-05-06T09:23:04.613 回答
22

使用 Apache Commons ListUtils.partition

org.apache.commons.collections4.ListUtils.partition(final List<T> list, final int size)
于 2019-05-28T14:09:40.377 回答
19

另一种方法是使用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]]

于 2017-01-06T07:16:03.057 回答
13

使用 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]]
于 2019-05-25T18:58:32.763 回答
8

我想出了这个:

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;
}
于 2016-03-16T13:31:44.863 回答
7

以下示例演示了 List 的分块:

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]]
于 2015-03-18T00:06:27.380 回答
6

这里有一个例子:

final AtomicInteger counter = new AtomicInteger();
final int partitionSize=3;
final List<Object> list=new ArrayList<>();
            list.add("A");
            list.add("B");
            list.add("C");
            list.add("D");
            list.add("E");
       
        
final Collection<List<Object>> subLists=list.stream().collect(Collectors.groupingBy
                (it->counter.getAndIncrement() / partitionSize))
                .values();
        System.out.println(subLists);

输入: [A、B、C、D、E]

输出: [[A, B, C], [D, E]]

您可以在此处找到示例: https ://e.printstacktrace.blog/divide-a-list-to-lists-of-n-size-in-Java-8/

于 2020-09-25T08:14:41.847 回答
6

这是 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();
}
于 2019-09-20T06:57:27.573 回答
5

还有另一个问题作为这个问题的重复而被关闭,但如果你仔细阅读它,它就会有微妙的不同。因此,如果有人(比如我)实际上想要将列表拆分为给定数量的几乎相同大小的子列表,请继续阅读。

我只是将这里描述的算法移植到 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);
}
于 2019-02-14T12:36:17.567 回答
4

使用来自网络的各种作弊,我来到了这个解决方案:

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

我们使用 count 来模拟一个正常的集合索引。
然后,我们将集合元素分组到桶中,使用代数商作为桶号。
最终的地图包含桶号作为,桶本身作为值。

然后,您可以轻松地对每个存储桶执行操作:

chunkedUsers.values().forEach( ... );
于 2015-09-11T09:53:03.877 回答
3

类似于没有流和库的 OP,但更简洁:

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;
}
于 2019-10-21T15:09:25.740 回答
1
List<T> batch = collection.subList(i,i+nextInc);
->
List<T> batch = collection.subList(i, i = i + nextInc);
于 2013-04-25T06:02:35.543 回答
1

请注意,它List#subList()返回基础集合的视图,这可能会在编辑较小的列表时导致意外后果 - 编辑将反映在原始集合中或可能会抛出ConcurrentModificationException.

于 2021-04-20T08:26:22.617 回答
0

您可以使用下面的代码来获取批次列表。

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

您需要导入 Google Guava 库才能使用上述代码。

于 2019-09-26T08:14:05.953 回答
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;
    }

}
于 2019-05-02T17:56:36.220 回答
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();

}
于 2019-07-24T07:03:20.853 回答
0

这是使用 vanilla java 和超级秘密模运算符的解决方案:)

鉴于块的内容/顺序无关紧要,这将是最简单的方法。(在为多线程准备东西时,通常并不重要,例如在哪个线程上处理哪些元素,只需要平均分配)。

public static <T> List<T>[] chunk(List<T> input, int chunkCount) {
    List<T>[] chunks = new List[chunkCount];

    for (int i = 0; i < chunkCount; i++) {
        chunks[i] = new LinkedList<T>();
    }

    for (int i = 0; i < input.size(); i++) {
        chunks[i % chunkCount].add(input.get(i));
    }

    return chunks;
}

用法:

    List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j");

    List<String>[] chunks = chunk(list, 4);

    for (List<String> chunk : chunks) {
        System.out.println(chunk);
    }

输出:

[a, e, i]
[b, f, j]
[c, g]
[d, h]
于 2021-12-07T20:19:12.663 回答
-2

import com.google.common.collect.Lists;

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

使用 Lists.partition(List,batchSize)。您需要Lists从谷歌通用包(com.google.common.collect.Lists)导入

它将返回 List of List<T>with 和每个元素的大小等于你的batchSize.

于 2019-06-18T09:48:06.313 回答