«1. Обзор

В этом уроке я покажу, как разделить список на несколько подсписков заданного размера.

Для относительно простой операции на удивление нет поддержки в стандартных API коллекций Java. К счастью, и в Guava, и в Apache Commons Collections операция реализована аналогичным образом.

Эта статья является частью серии «Java — Back to Basic» здесь, на Baeldung.

2. Использование Guava для разделения списка

Guava упрощает разделение списка на подсписки заданного размера — с помощью операции Lists.partition:

@Test
public void givenList_whenParitioningIntoNSublists_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
    List<List<Integer>> subSets = Lists.partition(intList, 3);

    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(3));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

3. Использование Guava для разделения коллекции ~ ~~ Разделение коллекции также возможно с помощью Guava:

Имейте в виду, что разделы представляют собой представления подсписков исходной коллекции, а это означает, что изменения в исходной коллекции будут отражены в разделах: ~~ ~

@Test
public void givenCollection_whenParitioningIntoNSublists_thenCorrect() {
    Collection<Integer> intCollection = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);

    Iterable<List<Integer>> subSets = Iterables.partition(intCollection, 3);

    List<Integer> firstPartition = subSets.iterator().next();
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(1, 2, 3);
    assertThat(firstPartition, equalTo(expectedLastPartition));
}

4. Использование Apache Commons Collections для разделения списка

@Test
public void givenListPartitioned_whenOriginalListIsModified_thenPartitionsChangeAsWell() {
    // Given
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
    List<List<Integer>> subSets = Lists.partition(intList, 3);

    // When
    intList.add(9);

    // Then
    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8, 9);
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

В последних выпусках Apache Commons Collections недавно также была добавлена ​​поддержка разделения списка:

Нет соответствующей опции для разделения необработанного Коллекция — аналогична разделу Guava Iterables.partition в коллекциях Commons.

@Test
public void givenList_whenParitioningIntoNSublists_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
    List<List<Integer>> subSets = ListUtils.partition(intList, 3);

    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(3));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

Наконец, здесь применимо то же самое предостережение — результирующий раздел является представлением исходного списка.

5. Использование Java8 для разделения списка

Теперь давайте посмотрим, как использовать Java8 для разделения нашего списка.

5.1. Collectors partitioningBy

Мы можем использовать Collectors.partitioningBy() для разделения списка на 2 подсписка — следующим образом:

Примечание. Результирующие разделы не являются представлением основного списка, поэтому любые изменения происходят в основной список не повлияет на разделы.

@Test
public void givenList_whenParitioningIntoSublistsUsingPartitionBy_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);

    Map<Boolean, List<Integer>> groups = 
      intList.stream().collect(Collectors.partitioningBy(s -> s > 6));
    List<List<Integer>> subSets = new ArrayList<List<Integer>>(groups.values());

    List<Integer> lastPartition = subSets.get(1);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(2));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

5.2. Collectors groupingBy

Мы также можем использовать Collectors.groupingBy(), чтобы разделить наш список на несколько разделов:

Примечание: так же, как и Collectors.partitioningBy() — результирующие разделы не будут затронуты изменениями. в основном списке.

@Test
public final void givenList_whenParitioningIntoNSublistsUsingGroupingBy_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);

    Map<Integer, List<Integer>> groups = 
      intList.stream().collect(Collectors.groupingBy(s -> (s - 1) / 3));
    List<List<Integer>> subSets = new ArrayList<List<Integer>>(groups.values());

    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(3));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

5.3. Разделить список по разделителю

Мы также можем использовать Java8 для разделения нашего списка по разделителю:

Примечание. Мы использовали «0» в качестве разделителя — мы сначала получили индексы всех «0». элементов в списке, то мы разбиваем список по этим индексам.

@Test
public void givenList_whenSplittingBySeparator_thenCorrect() {
    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());

    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(3));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

6. Заключение

Представленные здесь решения используют дополнительные библиотеки — Guava или библиотеку Apache Commons Collections. Оба они очень легкие и чрезвычайно полезные в целом, поэтому имеет смысл иметь один из них в пути к классам; однако, если это невозможно, здесь показано решение только для Java.

Реализацию всех этих примеров и фрагментов кода можно найти на GitHub — это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.

«