Partition a List in Java – 在Java中划分一个列表

最后修改: 2013年 12月 20日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll illustrate how to split a List into several sublists of a given size.

在这篇文章中,我们将说明如何将一个List分割成若干个特定大小的子List

For a relatively simple operation, there’s surprisingly no support in the standard Java collection APIs. Luckily, both Guava and the Apache Commons Collections have implemented the operation in a similar way.

对于一个相对简单的操作,在标准的Java集合API中竟然没有支持。幸运的是,GuavaApache Commons Collections都以类似方式实现了该操作。

This article is part of the “Java – Back to Basic” series here on Baeldung.

本文是Java – Back to Basic“系列的一部分,在Baeldung这里。

2. Use Guava to Partition the List

2.使用Guava对列表进行分区

Guava facilitates partitioning the List into sublists of a specified size via the Lists.partition operation:

Guava通过Lists.partition操作将List分割成指定大小的子列表。

@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. Use Guava to Partition a Collection

3.使用Guava来划分一个集合

Partitioning a Collection is also possible with 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));
}

Keep in mind that the partitions are sublist views of the original collection, which means that changes in the original collection will be reflected in the partitions:

请记住,分区是原始集合的子列表视图,这意味着原始集合的变化将反映在分区中。

@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. Use Apache Commons Collections to Partition the List

4.使用Apache Commons集合来划分列表

The latest releases of Apache Commons Collections have recently added support for partitioning a List as well:

Apache Commons Collections的最新版本最近也添加了支持对List进行分割。

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

Commons Collections doesn’t have a corresponding option to partition a raw Collection similar to the Guava Iterables.partition.

Commons Collections没有一个类似于Guava Iterables.partition的对应选项来分割一个原始的Collection

Finally, the same caveat applies here as well: the resulting partitions are views of the original List.

最后,同样的注意事项也适用于此:产生的分区是原始List的视图。

5. Use Java8 to Partition the List

5.使用Java8对列表进行分区

Now let’s see how to use Java8 to partition our List.

现在让我们来看看如何使用Java8来划分我们的List。

5.1. Collectors partitioningBy

5.1.收集器partitioningBy

We can use Collectors.partitioningBy() to split the list into 2 sublists:

我们可以使用Collectors.partitioningBy()将列表分成两个子列表。

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

Note: The resulting partitions aren’t a view of the main List, so any changes happening to the main List won’t affect the partitions.

注意:产生的分区不是主列表的一个视图,所以主列表的任何变化都不会影响分区。

5.2. Collectors groupingBy

5.2.收集器groupingBy

We can also use Collectors.groupingBy() to split our list into multiple partitions:

我们还可以使用 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));
}

Note: Just as with Collectors.partitioningBy(), the resulting partitions won’t be affected by changes in the main List.

注意:就像使用Collectors.partitioningBy()一样,产生的分区不会受到主List中变化的影响。

5.3. Split the List by Separator

5.3.通过分隔符分割列表

We can also use Java8 to split our List by separator:

我们也可以用Java8来分割我们的List,用分隔符。

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

Note: We used “0” as separator. We first obtained the indices of all “0” elements in the List, and then we split the List on these indices.

注意:我们用 “0 “作为分隔符。我们首先获得List中所有 “0 “元素的索引,然后根据这些索引分割List

6. Conclusion

6.结论

The solutions presented here make use of additional libraries, namely Guava and the Apache Commons Collections. Both of these are very lightweight and extremely useful overall, so it makes perfect sense to have one of them on the classpath. However, if that’s not an option, a Java only solution is shown here.

这里介绍的解决方案使用了额外的库,即Guava和Apache Commons集合。这两个库都是非常轻量级的,而且总体上非常有用,所以在classpath上有一个这样的库是非常合理的。但是,如果这不是一种选择,这里显示了一种仅使用 Java 的解决方案

The implementation of all these examples and code snippets can be found over on GitHub. This is a Maven-based project, so it should be easy to import and run as it is.

所有这些例子和代码片段的实现都可以在GitHub上找到这是一个基于Maven的项目,所以应该很容易按原样导入和运行。