在 Java 中对List进行分区

1. 概述

在本文中,我们将说明如何将一个列表拆分为多个给定大小的子列表。

对于这个相对简单的操作,标准 Java 集合 API 竟然不支持它。幸运的是,GuavaApache-Commons 都提供了对应的 API 。

2. 使用 Guava 对 List 进行分区

Guava 通过Lists.partition 操作将 List 划分为指定大小 的子列表:

Java 复制代码
List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
List<List<Integer>> subSets = Lists.partition(intList, 3);
subSets.forEach(s -> log.info("{}", s));

List<Integer> lastPartition = subSets.get(2);
List<Integer> expectedLastPartition = Lists.newArrayList(7, 8);

Assertions.assertEquals(subSets.size(), 3);
Assertions.assertEquals(lastPartition, expectedLastPartition);

下面是我们得到的输出结果:

java 复制代码
[1, 2, 3]
[4, 5, 6]
[7, 8]

3. 使用 Guava 对集合进行分区

Guava 也可以对集合进行分区:

Java 复制代码
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.newArrayList(1, 2, 3);
Assertions.assertEquals(firstPartition, expectedLastPartition);

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

Java 复制代码
@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 Collections 对列表进行分区

Apache Commons Collections 的最新版本最近也添加了对列表分区的支持:

Java 复制代码
@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 没有相应的选项来对原始集合进行分区, 类似于 Guava Iterables.partition。

最后,同样的警告也适用于此:生成的分区是原始列表的视图。

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

现在让我们看看如何使用 Java8 对我们的 List 进行分区。

5.1 收集器分区方式

我们可以使用Collectors.partitioningBy() 将列表拆分为 2 个子列表:

Java 复制代码
@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() 将我们的列表分成多个分区:

Java 复制代码
@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 按分隔符拆分我们的列表:

Java 复制代码
@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"作为分隔符。我们首先获取了 List 中所有"0"元素的索引,然后我们根据这些索引拆分了List

6. 结论

此处介绍的解决方案使用了额外的库,即 Guava 和 Apache Commons Collections。这两者都非常轻量级并且总体上非常有用,因此将其中之一放在类路径中是非常有意义的。但是,如果这不是一个选项,那么此处显示了仅 Java 的解决方案。

相关推荐
救救孩子把11 分钟前
深入理解 Java 对象的内存布局
java
落落落sss13 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节19 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭26 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
我行我素,向往自由32 分钟前
速成java记录(上)
java·速成
一直学习永不止步38 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明38 分钟前
面试知识储备-多线程
java·面试·职场和发展
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
2401_854391081 小时前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端