Java List split

Долго валялось в закладках на SO – и пригодилось, наконец. Обычно – или уже была “в коробке” Guava с Lists.partition(), или – Apache Commons и его ListUtils.partition().

Деление на подмножества (каплю доработано относительно исходника на SO):

// https://stackoverflow.com/a/30072617
public static <T> Stream<List<T>> listPartition(@NotNull List<T> source, @Positive int length) {
    requireNonNull(source, "Source list must not be null");
    if (length <= 0) {
        throw new IllegalArgumentException("Partitions length must be greater than zero");
    }
    int size = source.size();
    if (size == 0) {
        return Stream.empty();
    }
    if (size <= length) {
        return Stream.of(source);
    }
    int fullChunks = (size - 1) / length;
    return IntStream.range(0, fullChunks + 1)
        .mapToObj(n -> source.subList(n * length, n == fullChunks ? size : (n + 1) * length));
}

Несколько тестов

@DisplayName("Split list to two partitions")
@Test
void listPartition_1() {
    List<Integer> source = List.of(1, 2, 3);

    var partitions = listPartition(source, 2).collect(Collectors.toList());

    assertThat(partitions).containsExactlyElementsOf(List.of(List.of(1, 2), List.of(3)));
}

@DisplayName("Split list to one partition - source size lesser than or equal to partition length")
@ParameterizedTest
@ValueSource(ints = {3, 4})
void listPartition_2(int length) {
    List<Integer> source = List.of(1, 2, 3);

    var partitions = listPartition(source, length).collect(Collectors.toList());

    assertThat(partitions).containsExactlyElementsOf(List.of(List.of(1, 2, 3)));
}

@DisplayName("Empty result - source is empty")
@Test
void listPartition_3() {
    List<Integer> source = List.of();

    var partitions = listPartition(source, 2).collect(Collectors.toList());

    assertThat(partitions).isEmpty();
}

@DisplayName("Thrown IAE - wrong 'length' arg passed")
@ParameterizedTest
@ValueSource(ints = {-1, 0})
void listPartition_4(int length) {
    List<Integer> source = List.of();

    assertThatThrownBy(() -> listPartition(source, length))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("Partitions length must be greater than zero");
}

@DisplayName("Thrown NPE - source list is null")
@Test
void listPartition_5() {
    List<Integer> source = null;

    assertThatThrownBy(() -> listPartition(source, 2))
        .isInstanceOf(NullPointerException.class)
        .hasMessage("Source list must not be null");
}