Список разделов Java 8
Можно ли разбить список в чистом Jdk8 на равные куски (подсписки).
Я знаю, что это возможно с помощью класса Guava Lists, но можем ли мы сделать это с чистым Jdk? Я не хочу добавлять новые фляги в свой проект, только для одного варианта использования.
СОЛЮТОНЫ:
Лучшее решение до сих пор представлял Тагир-Валеев:
Я также нашел три другие возможности, но они предназначены только для нескольких случаев:
1.Collectors.partitioningBy() разделить список на 2 подсписка - следующим образом:
intList.stream().collect(Collectors.partitioningBy(s -> s > 6));
List<List<Integer>> subSets = new ArrayList<List<Integer>>(groups.values());
2.Collectors.groupingBy(), чтобы разделить наш список на несколько разделов:
Map<Integer, List<Integer>> groups =
intList.stream().collect(Collectors.groupingBy(s -> (s - 1) / 3));
List<List<Integer>> subSets = new ArrayList<List<Integer>>(groups.values());
3. Разделить на разделитель:
List<Integer> intList = Lists.newArrayList(1, 2, 3, 0, 4, 5, 6, 0, 7, 8);
int[] indexes =
Stream.of(IntStream.of(-1), IntStream.range(0, intList.size())
.filter(i -> intList.get(i) == 0), IntStream.of(intList.size()))
.flatMapToInt(s -> s).toArray();
List<List<Integer>> subSets =
IntStream.range(0, indexes.length - 1)
.mapToObj(i -> intList.subList(indexes[i] + 1, indexes[i + 1]))
.collect(Collectors.toList());
4. Использование потоков + счетчик источника:
final List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7);
final int chunkSize = 3;
final AtomicInteger counter = new AtomicInteger();
final Collection<List<Integer>> result = numbers.stream()
.collect(Collectors.groupingBy(it -> counter.getAndIncrement() / chunkSize))
.values();
Ответы
Ответ 1
Я попробовал свое собственное решение с коллектором на заказ. Я надеюсь, что кто-то найдет это полезным или поможет мне улучшить его.
class PartitioningCollector<T> implements Collector<T, List<List<T>>, List<List<T>>> {
private final int batchSize;
private final List<T> batch;
public PartitioningCollector(int batchSize) {
this.batchSize = batchSize;
this.batch = new ArrayList<>(batchSize);
}
@Override
public Supplier<List<List<T>>> supplier() {
return LinkedList::new;
}
@Override
public BiConsumer<List<List<T>>, T> accumulator() {
return (total, element) -> {
batch.add(element);
if (batch.size() >= batchSize) {
total.add(new ArrayList<>(batch));
batch.clear();
}
};
}
@Override
public BinaryOperator<List<List<T>>> combiner() {
return (left, right) -> {
List<List<T>> result = new ArrayList<>();
result.addAll(left);
result.addAll(left);
return result;
};
}
@Override
public Function<List<List<T>>, List<List<T>>> finisher() {
return result -> {
if (!batch.isEmpty()) {
result.add(new ArrayList<>(batch));
batch.clear();
}
return result;
};
}
@Override
public Set<Characteristics> characteristics() {
return emptySet();
}
}
Ответ 2
Это легко сделать с помощью subList()
:
List<String> collection = new ArrayList<>(21);
// fill collection
int chunkSize = 10;
List<List<String>> lists = new ArrayList<>();
for (int i = 0; i < collection.size(); i += chunkSize) {
int end = Math.min(collection.size(), i + chunkSize);
lists.add(collection.subList(i, end));
}
Ответ 3
Попробуйте использовать этот код, он использует Java 8:
public static Collection<List<Integer>> splitListBySize(List<Integer> intList, int size) {
if (!intList.isEmpty() && size > 0) {
final AtomicInteger counter = new AtomicInteger(0);
return intList.stream().collect(Collectors.groupingBy(it -> counter.getAndIncrement() / size)).values();
}
return null;
}
Ответ 4
private final String dataSheet = "103343262,6478342944, 103426540,84528784843, 103278808,263716791426, 103426733,27736529279,
103426000,27718159078, 103218982,19855201547, 103427376,27717278645,
103243034,81667273413";
final int chunk = 2;
AtomicInteger counter = new AtomicInteger();
Collection<List<String>> chuncks= Arrays.stream(dataSheet.split(","))
.map(String::trim)
.collect(Collectors.groupingBy(i->counter.getAndIncrement()/chunk))
.values();
результат:
pairs =
"103218982" -> "19855201547"
"103278808" -> "263716791426"
"103243034" -> "81667273413"
"103426733" -> "27736529279"
"103426540" -> "84528784843"
"103427376" -> "27717278645"
"103426000" -> "27718159078"
"103343262" -> "6478342944"
Нам нужно сгруппировать каждые 2 элемента в пары ключ-значение, поэтому разделим список на куски по 2, (counter.getAndIncrement()/2) приведет к тому же числу каждые 2 попадания, например:
IntStream.range(0,6).forEach((i)->System.out.println(counter.getAndIncrement()/2));
prints:
0
0
1
1
2
2
Вы можете откорректировать размер чанка по размеру списков разделов.