在 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 的解决方案。

相关推荐
FreeTinker4 分钟前
Java文件服务器的技术选型与实现路径:从嵌入式工具到企业级系统
java·服务器·开发语言
ly768917 分钟前
Java 21 到 Java 26 新特性详解:虚拟线程、模式匹配、结构化并发、HTTP/3 与 AOT
java·jvm
我星期八休息35 分钟前
Linux I/O多路转接—epoll
java·linux·运维·服务器·开发语言·jvm·算法
学长毕业设计1 小时前
基于springboot的云南中草药知识普及管理网站的设计与开发(源码+文档+讲解视频)
java·spring boot·后端
大模型码小白1 小时前
AI 提示词专栏:使用系统指令(System Prompt)实现全局约束
java·大数据·运维·开发语言·人工智能·python·prompt
cxoptics1 小时前
蓝宝石的轴向在窗片和偏振片中起的作用
java
wuminyu1 小时前
Java FFM处理网络读写事件源码剖析
java·linux·c语言·jvm·c++
小江的记录本2 小时前
【AI Agent】《2026年9月 AI Agent全栈开发技术选型专项面试宝典》
java·spring boot·python·spring·ai·面试·ai编程
梦梦代码精2 小时前
《LikeShop全产品技术硬核拆解:ThinkPHP8+Vue3+UniApp架构,私有化部署与二次开发实战指南》
java·低代码·系统架构·php·开源软件
一木 之林2 小时前
C/C++ 面向对象编程(OOP)(进阶)
java·c语言·c++