Bir listeyi toplu olarak bölmek için ortak bir Java yardımcı programı var mı?


141

Kendime verilen büyüklükteki gruplar halinde bir liste kırmak için bir yardımcı program yazdım. Sadece bunun için herhangi bir apache ortak kullanımı olup olmadığını bilmek istedim.

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

Zaten aynı için mevcut bir yardımcı program varsa lütfen bana bildirin.


4
Bunun konu dışı olduğundan emin değilim. Soru "bunu hangi kütüphanenin yaptığını" değil "apache ortak araçlarıyla nasıl yapabilirim" dir.
Florian F

@FlorianF Sana katılıyorum. Bu soru ve cevapları çok faydalıdır ve küçük bir düzenleme ile iyi bir şekilde kaydedilebilir. Aceleyle kapatmak tembel bir eylemdi.
Endery

Güzel bir sınıf ve karşılaştırmalı değerlendirmeler içeren yararlı blog yazısı burada bulundu: e.printstacktrace.blog/…
Benj

Yanıtlar:


250

Check out dan Google Guava : Lists.partition(java.util.List, int)

Her biri aynı boyutta olan bir listenin ardışık alt listelerini döndürür (son liste daha küçük olabilir). Örneğin, bölüm [a, b, c, d, e]boyutu 3 olan bir listenin bölümlendirilmesi [[a, b, c], [d, e]]- orijinal sırayla, üç ve iki öğeden oluşan iki iç liste içeren bir dış liste oluşturur.


bağlantı partition documentation ve bağlantı code example
Austin Haws

16
Apache yaygın kullanıcıları için, bu işlev de mevcuttur: commons.apache.org/proper/commons-collections/apidocs/org/…
Xavier Portebois

3
Bir liste ile çalışıyorsanız "Apache Commons Collections 4" kütüphanesini kullanıyorum. ListUtils sınıfında bir bölümleme yöntemi vardır: ... int targetSize = 100; Liste <Integer> largeList = ... Liste <Liste <Integer>> output = ListUtils.partition (largeList, targetSize); Bu yöntem code.google.com/p/guava-libraries
adresinden

1
Teşekkür ederim. Bunun Java'da ne kadar zor olduğuna inanamıyorum.
Uzun Saç Amca

51

Bir Java-8 toplu akışı üretmek istiyorsanız, aşağıdaki kodu deneyebilirsiniz:

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

Çıktı:

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]

Bu yaklaşımda nasıl kırılır, devam eder veya geri dönerim?
Miral

15

Diğer bir yaklaşım, Collectors.groupingByendekslerin kullanılması ve ardından gruplanmış endekslerin gerçek unsurlarla eşleştirilmesidir:

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

Çıktı:

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

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


1
@Sebien Bu genel durum için işe yarar. groupingByUnsurları üzerinde yapılır IntStream.range, değil liste elemanları. Bkz. Örneğin ideone.com/KYBc7h .
Radiodef

@MohammedElrashidy Sebien yorumlarını kaldırdı, artık sizinkini kaldırabilirsiniz.
Albert Hendriks

7

Ben bununla geldim:

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()ile hasNextkoşulla birlikte kullanabilirsiniz . Böylece, yönteminizin kodunu basitleştirebilirsiniz:

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

Kullanarak {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, sonucu şöyle getBatches(numbers, 4)olacaktır:

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

5

Aşağıdaki örnek bir Listenin parçalanmasını gösterir:

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

Çıktı:

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

Bunun bir kopyası olarak kapatılmış başka bir soru vardı, ama yakından okursanız, bu oldukça farklı. Yani birisinin (benim gibi) bir listeyi neredeyse eşit sayıda alt listeye bölmek istemesi durumunda okumaya devam edin.

Burada açıklanan algoritmayı Java'ya taşıdım.

@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

Web'den çeşitli hileler kullanarak, bu çözüme geldim:

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

Normal bir toplama endeksini taklit etmek için count kullanıyoruz.
Ardından, cebirsel bölümü bölüm numarası olarak kullanarak toplama öğelerini bölümler halinde gruplandırırız.
Nihai harita , kova numarasını anahtar olarak içerir; grubun kendisine değer verir.

Daha sonra, kovaların her birinde aşağıdakilerle kolayca bir işlem yapabilirsiniz:

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

4
Saymak için bir kullanabilirsiniz AtomicInteger.
jkschneider

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

1

Akışları ve kütüphaneleri olmayan OP'ye benzer, ancak kısaca:

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

Bunu çözmek için başka bir yaklaşım, soru:

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'deki bir astar:

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+ için basit bir çözüm:

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

Liste grubunu almak için aşağıdaki kodu kullanabilirsiniz.

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

Yukarıdaki kodu kullanmak için Google Guava kütüphanesini içe aktarmanız gerekir.


-1

import com.google.common.collect.Lists;

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

Lists.partition (List, batchSize) öğesini kullanın. ListsGoogle ortak paketinden ( com.google.common.collect.Lists) içe aktarmanız gerekir

İle listesi List<T>ve her öğenin boyutuna eşit olacaktır batchSize.


subList(startIndex, endIndex)İstenilen dizine göre listeyi kırmak için kendi yöntemlerini de kullanabilirsiniz .
v87278
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.