Có một tiện ích Java phổ biến để chia một danh sách thành các đợt không?


140

Tôi đã viết cho mình một tiện ích để chia một danh sách thành các lô có kích thước nhất định. Tôi chỉ muốn biết nếu đã có bất kỳ dấu phẩy apache nào cho việc này.

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

Xin vui lòng cho tôi biết nếu có bất kỳ tiện ích hiện có nào cho cùng.


4
Không chắc đây là lạc đề. Câu hỏi không phải là "thư viện nào làm việc này" mà là "làm thế nào tôi có thể làm điều này với những tiện ích chung của apache".
Florian F

@FlorianF Tôi đồng ý với bạn. Câu hỏi này và câu trả lời của nó rất hữu ích, và nó có thể được lưu lại với một chỉnh sửa nhỏ. Đó là một hành động lười biếng để đóng nó vội vàng.
Endery

Tìm thấy bài viết trên blog hữu ích với lớp đẹp và điểm chuẩn ở đây: e.printstacktrace.blog/...
Benj

Câu trả lời:


249

Kiểm tra từ Google Guava : Lists.partition(java.util.List, int)

Trả về danh sách con liên tiếp của một danh sách, mỗi danh sách có cùng kích thước (danh sách cuối cùng có thể nhỏ hơn). Ví dụ: phân vùng một danh sách chứa [a, b, c, d, e]với kích thước phân vùng là 3 sản lượng [[a, b, c], [d, e]]- một danh sách bên ngoài chứa hai danh sách bên trong gồm ba và hai phần tử, tất cả theo thứ tự ban đầu.


liên kết partition documentationliên kết code example
Austin Haws

16
Đối với người dùng thông thường apache, chức năng này cũng có sẵn: commons.apache.org/proper/commons-collections/apidocs/org/
Kẻ

3
Nếu bạn đang làm việc với một danh sách tôi sử dụng thư viện "Bộ sưu tập Apache Commons 4". Nó có một phương thức phân vùng trong lớp ListUtils: ... int targetSize = 100; Danh sách <Integer> LargeList = ... Danh sách <List <Integer >> output = ListUtils.partition (LargeList, targetSize); Phương pháp này được điều chỉnh từ code.google.com/p/guava-lologists
Swapnil Jaju

1
Cảm ơn bạn. Tôi không thể tin được việc này khó như thế nào trong Java.
Chú tóc dài

51

Trong trường hợp bạn muốn tạo một luồng các đợt Java-8, bạn có thể thử đoạn mã sau:

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

Đầu ra:

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]

Làm thế nào để tôi phá vỡ, tiếp tục hoặc trở lại trong phương pháp này?
Miral

15

Một cách tiếp cận khác là sử dụng Collectors.groupingBycác chỉ mục và sau đó ánh xạ các chỉ mục được nhóm thành các yếu tố thực tế:

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

Đầu ra:

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

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


1
@Sebien Điều này không hoạt động cho trường hợp chung. Điều groupingBynày được thực hiện trên các yếu tố của IntStream.range, không phải các yếu tố danh sách. Xem ví dụ: ideone.com/KYBc7h .
Radiodef

@MohammedElrashidy Sebien đã xóa bình luận của họ, bây giờ bạn có thể xóa bình luận của bạn.
Albert Hendriks

7

Tôi đã đưa ra cái này:

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

Với Java 9, bạn có thể sử dụng IntStream.iterate()với hasNextđiều kiện. Vì vậy, bạn có thể đơn giản hóa mã của phương thức của bạn để này:

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

Sử dụng {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, kết quả của getBatches(numbers, 4)sẽ là:

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

5

Ví dụ sau đây cho thấy sự phân chia danh sách:

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

Đầu ra:

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

một câu hỏi khác được đóng lại là một bản sao của câu hỏi này, nhưng nếu bạn đọc kỹ, nó sẽ khác một cách tinh tế. Vì vậy, trong trường hợp ai đó (như tôi) thực sự muốn chia một danh sách thành một số danh sách phụ có kích thước gần như bằng nhau , hãy đọc tiếp.

Tôi chỉ đơn giản chuyển thuật toán được mô tả ở đây sang 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

Sử dụng nhiều mánh gian lận khác nhau từ trang web, tôi đã tìm đến giải pháp này:

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

Chúng tôi sử dụng đếm để bắt chước một chỉ số bộ sưu tập bình thường.
Sau đó, chúng tôi nhóm các phần tử tập hợp trong các nhóm, sử dụng thương số đại số làm số nhóm.
Bản đồ cuối cùng chứa khóa chính là số xô, như giá trị của chính nhóm đó.

Sau đó, bạn có thể dễ dàng thực hiện một thao tác trên mỗi thùng với:

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

4
Có thể sử dụng một AtomicIntegersố lượng.
jkschneider

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

1

Tương tự như OP không có luồng và lib, nhưng conciser:

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

Một cách tiếp cận khác để giải quyết vấn đề này:

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

Một lớp lót trong Java 8 sẽ là:

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

Đây là một giải pháp đơn giản cho 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

Bạn có thể sử dụng mã dưới đây để có được danh sách lô.

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

Bạn cần nhập thư viện Google Guava để sử dụng mã trên.


-1

import com.google.common.collect.Lists;

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

Sử dụng Lists.partition (List, batchSize). Bạn cần nhập Liststừ gói chung google ( com.google.common.collect.Lists)

Nó sẽ trả về Danh sách List<T>với và kích thước của mọi phần tử bằng với bạn batchSize.


Bạn cũng có thể sử dụng subList(startIndex, endIndex)phương pháp riêng của họ để phá vỡ danh sách dựa trên chỉ mục bắt buộc.
v87278
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.