Перейти к основному содержимому

Разделить список в Java

· 4 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Обзор

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

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

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

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

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

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

4. Используйте коллекции Apache Commons для разделения списка

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

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

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

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

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

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

5.1. Разделение коллекторовПо ``

Мы можем использовать 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() , чтобы разделить наш список на несколько разделов:

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

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

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

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

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

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

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

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

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