1. 概述

本文将探讨在Java中如何将嵌套集合(如List<List>)扁平化为单层集合的几种方法。这种操作在数据处理中非常常见,尤其在处理多层结构时特别实用。

2. 嵌套集合示例

假设我们有一个字符串类型的嵌套列表:

List<List<String>> nestedList = asList(
  asList("one:one"), 
  asList("two:one", "two:two", "two:three"), 
  asList("three:one", "three:two", "three:three", "three:four"));

这个结构包含三个子列表,每个子列表包含不同数量的字符串元素。

3. 使用forEach扁平化处理

最直接的方式是使用forEach结合方法引用进行扁平化:

public <T> List<T> flattenListOfListsImperatively(
    List<List<T>> nestedList) {
    List<T> ls = new ArrayList<>();
    nestedList.forEach(ls::addAll);
    return ls;
}

核心要点:

  • 创建目标空列表
  • 通过forEach遍历每个子列表
  • 使用addAll将子列表元素合并到目标列表

测试验证:

@Test
public void givenNestedList_thenFlattenImperatively() {
    List<String> ls = flattenListOfListsImperatively(nestedList);
    
    assertNotNull(ls);
    assertTrue(ls.size() == 8);
    assertThat(ls, IsIterableContainingInOrder.contains(
      "one:one",
      "two:one", "two:two", "two:three", "three:one",
      "three:two", "three:three", "three:four"));
}

4. 使用flatMap扁平化处理

更优雅的方式是使用Stream API的flatMap方法:

public <T> List<T> flattenListOfListsStream(List<List<T>> list) {
    return list.stream()
      .flatMap(Collection::stream)
      .collect(Collectors.toList());    
}

核心要点:

  • 将嵌套列表转为Stream
  • flatMap将每个子列表转为流并合并
  • 最终收集为List

测试验证:

@Test
public void givenNestedList_thenFlattenFunctionally() {
    List<String> ls = flattenListOfListsStream(nestedList);
    
    assertNotNull(ls);
    assertTrue(ls.size() == 8);
}

⚠️ 注意: flatMap是函数式编程的典型用法,代码更简洁但需要理解Stream操作链。

5. 总结

处理嵌套集合扁平化时,推荐以下两种方式:

  1. 命令式风格(forEach + addAll)

    • 适合简单场景
    • 代码直观易读
  2. 函数式风格(flatMap)

    • 更符合现代Java编程范式
    • 适合复杂流式处理

两种方式都能高效完成任务,选择哪种取决于具体场景和团队编码风格。对于有经验的开发者,建议优先掌握flatMap方案,它在后续链式操作中更具扩展性。


原始标题:Flattening Nested Collections in Java | Baeldung