Существует ли общая утилита Java для разбивки списка на пакеты?
Я написал себе утилиту, чтобы разбить список на партии заданного размера. Я просто хотел знать, есть ли уже какие-либо утилиты Apache Commons для этого.
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;
}
Пожалуйста, дайте мне знать, если есть уже существующая утилита для того же.
Ответы
Ответ 1
Отъезд Lists.partition(java.util.List, int)
из Google Guava:
Возвращает последовательные подсписки списка, каждый из которых имеет одинаковый размер (последний список может быть меньше). Например, разбиение списка, содержащего [a, b, c, d, e]
с размером раздела 3, дает [[a, b, c]
, [d, e]]
- внешний список, содержащий два внутренних списка из трех и двух элементов, все в исходном порядке.
Ответ 2
Если вы хотите создать пакет партий 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]
Ответ 3
Другим подходом является использование индексов 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]]
Ответ 4
Я придумал это:
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;
}
Ответ 5
Следующий пример демонстрирует разбиение списка:
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]]
Ответ 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]]
Ответ 7
Используя различные читы из Интернета, я пришел к этому решению:
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( ... );
Ответ 8
Был еще один вопрос, который был закрыт как дубликат этого, но если вы внимательно его прочитаете, он немного отличается. Поэтому, если кто-то (например, я) действительно хочет разделить список на заданное количество почти одинаковых по размеру подсписков, тогда читайте дальше.
Я просто перенес описанный здесь алгоритм на 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);
}
Ответ 9
List<T> batch = collection.subList(i,i+nextInc);
->
List<T> batch = collection.subList(i, i = i + nextInc);
Ответ 10
Используйте Apache Commons ListUtils.partition.
Ответ 11
Другой подход к решению этого вопроса:
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;
}
}
Ответ 12
import com.google.common.collect.Lists;
List<List<T>> batches = Lists.partition(List<T>,batchSize)
Используйте Lists.partition(List, batchSize). Вам нужно импортировать Lists
из общего пакета Google (com.google.common.collect.Lists
)
Он вернет List of List<T>
с размером каждого элемента, равным вашему batchSize
.
Ответ 13
Однострочник в 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();
}
Ответ 14
Вот простое решение для 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();
}
Ответ 15
Вы можете использовать приведенный ниже код, чтобы получить пакет списка.
Iterable<List<T>> batchIds = Iterables.partition(list, batchSize);
Вам нужно импортировать библиотеку Google Guava, чтобы использовать приведенный выше код.
Ответ 16
Похож на 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;
}