1. 概述
List是Java中常用的数据结构。有时我们需要嵌套的List结构(如List<List<T>>
)来满足特定需求。本文将深入探讨这种"List的List"数据结构,并演示常见操作技巧。
2. 数组 vs 嵌套List
我们可以将"List的List"视为二维矩阵。当需要组合多个List<T>
对象时,有两种选择:
- 数组实现:
List<T>[]
- List实现:
List<List<T>>
选择建议
- 数组:
get/set
操作时间复杂度O(1),但长度固定,插入/删除元素成本高 - List:插入/删除操作时间复杂度O(1),灵活性更高,
get/set
操作略慢(但ArrayList等实现基于数组,性能差异不明显)
踩坑提醒:除非在性能敏感场景且第一维度大小固定(如不增删内部List),否则优先选择
List<List<T>>
以获得更好的灵活性。本文重点讨论List<List<T>>
。
3. 嵌套List的常见操作
为方便演示,我们以List<List<String>>
为例。先创建一个辅助方法打印嵌套List内容:
private void printListOfLists(List<List<String>> listOfLists) {
System.out.println("\n List of Lists ");
System.out.println("-------------------------------------");
listOfLists.forEach(innerList -> {
String line = String.join(", ", innerList);
System.out.println(line);
});
}
3.1 初始化嵌套List
从CSV文件导入数据到List<List<T>>
。示例CSV内容(resources/listoflists/example.csv
):
Linux, Microsoft Windows, Mac OS, Delete Me
Kotlin, Delete Me, Java, Python
Delete Me, Mercurial, Git, Subversion
读取文件并初始化的方法:
private List<List<String>> getListOfListsFromCsv() throws URISyntaxException, IOException {
List<String> lines = Files.readAllLines(Paths.get(getClass().getResource("/listoflists/example.csv")
.toURI()));
List<List<String>> listOfLists = new ArrayList<>();
lines.forEach(line -> {
List<String> innerList = new ArrayList<>(Arrays.asList(line.split(", ")));
listOfLists.add(innerList);
});
return listOfLists;
}
关键点:用
new ArrayList<>()
包装Arrays.asList()
,避免返回不可变List(后续需要修改内部List)。
验证初始化结果:
List<List<String>> listOfLists = getListOfListsFromCsv();
assertThat(listOfLists).hasSize(3);
assertThat(listOfLists.stream()
.map(List::size)
.collect(Collectors.toSet())).hasSize(1)
.containsExactly(4);
printListOfLists(listOfLists);
输出:
List of Lists
-------------------------------------
Linux, Microsoft Windows, Mac OS, Delete Me
Kotlin, Delete Me, Java, Python
Delete Me, Mercurial, Git, Subversion
3.2 操作外层List
将List<List<T>>
视为普通List<T>
操作即可。示例:在索引2处插入新List:
List<List<String>> listOfLists = getListOfListsFromCsv();
List<String> newList = new ArrayList<>(Arrays.asList("Slack", "Zoom", "Microsoft Teams", "Telegram"));
listOfLists.add(2, newList);
assertThat(listOfLists).hasSize(4);
assertThat(listOfLists.get(2)).containsExactly("Slack", "Zoom", "Microsoft Teams", "Telegram");
printListOfLists(listOfLists);
输出:
List of Lists
-------------------------------------
Linux, Microsoft Windows, Mac OS, Delete Me
Kotlin, Delete Me, Java, Python
Slack, Zoom, Microsoft Teams, Telegram
Delete Me, Mercurial, Git, Subversion
3.3 操作内层List
精准操作指定内层List
通过索引获取目标List后操作:
List<String> innerList = listOfLists.get(x);
// innerList.add(), remove() ...
批量操作所有内层List
使用Stream API或循环遍历。示例:删除所有"Delete Me"条目:
List<List<String>> listOfLists = getListOfListsFromCsv();
listOfLists.forEach(innerList -> innerList.remove("Delete Me"));
assertThat(listOfLists.stream()
.map(List::size)
.collect(Collectors.toSet())).hasSize(1)
.containsExactly(3);
printListOfLists(listOfLists);
输出:
List of Lists
-------------------------------------
Linux, Microsoft Windows, Mac OS
Kotlin, Java, Python
Mercurial, Git, Subversion
3.4 计算嵌套List的尺寸
场景1:内层List的数量
直接调用size()
:
List<List<String>> listOfLists = getListOfListsFromCsv();
assertThat(listOfLists).hasSize(3);
场景2:所有内层List的元素总数
使用Stream API计算总和:
int totalElements = listOfLists.stream().mapToInt(List::size).sum();
assertThat(totalElements).isEqualTo(12);
简单粗暴:
mapToInt()
将每个内层List转为其尺寸的IntStream
,再调用sum()
求和。
4. 总结
本文详细介绍了Java中嵌套List(List<List<T>>
)的使用场景和核心操作:
- 选择依据:优先使用
List<List<T>>
而非数组实现,除非有特殊性能需求 - 初始化技巧:注意可变性处理,用
ArrayList
包装Arrays.asList()
- 操作分层:
- 外层List:直接使用标准List方法
- 内层List:通过索引精准操作或Stream批量处理
- 尺寸计算:区分内层List数量和总元素数,后者用Stream API高效计算
掌握这些操作能显著提升处理复杂数据结构的效率,尤其在数据导入导出、表格处理等场景中。