什么是二维 ArrayList?
在 Java 中,标准的 ArrayList 是一个一维的、可以动态增长的对象列表,一个“二维 ArrayList”本质上是一个 ArrayList,它的每一个元素又是一个 ArrayList。

你可以把它想象成一个“数组的数组”,但它的每一行(内部的 ArrayList)都可以拥有不同的长度,并且可以动态地增加或删除行和列,这比传统的二维数组(int[][])更加灵活。
如何声明和初始化二维 ArrayList
基本语法
// 声明一个外部的 ArrayList,它内部的元素是另一个 ArrayList // 我们会指定内部的 ArrayList 存储的数据类型,Integer, String 等 ArrayList<ArrayList<String>> twoDArrayList; // 初始化 twoDArrayList = new ArrayList<ArrayList<String>>();
完整的初始化示例
一个更完整的初始化过程,通常包括创建内部的 ArrayList 并添加到外部的 ArrayList 中。
import java.util.ArrayList;
public class TwoDArrayListExample {
public static void main(String[] args) {
// 1. 声明并初始化外部的 ArrayList
ArrayList<ArrayList<Integer>> matrix = new ArrayList<>();
// 2. 创建并添加第一行 (内部 ArrayList)
ArrayList<Integer> row1 = new ArrayList<>();
row1.add(1);
row1.add(2);
row1.add(3);
matrix.add(row1);
// 3. 创建并添加第二行
ArrayList<Integer> row2 = new ArrayList<>();
row2.add(4);
row2.add(5);
matrix.add(row2);
// 4. 创建并添加第三行
ArrayList<Integer> row3 = new ArrayList<>();
row3.add(6);
row3.add(7);
row3.add(8);
row3.add(9); // 这一行的长度比前两行长
matrix.add(row3);
// matrix 看起来像这样:
// [ [1, 2, 3],
// [4, 5],
// [6, 7, 8, 9] ]
System.out.println("完整的二维 ArrayList: " + matrix);
}
}
使用循环初始化(更优雅的方式)
我们可以使用循环来简化初始化过程,特别是当所有行的初始长度相同时。
import java.util.ArrayList;
public class TwoDArrayListInitialization {
public static void main(String[] args) {
int rows = 3;
int cols = 4;
ArrayList<ArrayList<String>> grid = new ArrayList<>();
for (int i = 0; i < rows; i++) {
// 为每一行创建一个新的 ArrayList
ArrayList<String> currentRow = new ArrayList<>();
for (int j = 0; j < cols; j++) {
// 向当前行添加元素
currentRow.add("Cell-" + i + "-" + j);
}
// 将当前行添加到外部的 ArrayList 中
grid.add(currentRow);
}
System.out.println("使用循环初始化的网格: " + grid);
// 输出: [[Cell-0-0, Cell-0-1, Cell-0-2, Cell-0-3], [Cell-1-0, ...], ...]
}
}
如何访问和修改元素
访问二维 ArrayList 的元素需要使用两次 get() 方法:第一次获取行,第二次获取该行中的列。

import java.util.ArrayList;
public class AccessElements {
public static void main(String[] args) {
ArrayList<ArrayList<String>> data = new ArrayList<>();
data.add(new ArrayList<>(List.of("A", "B", "C")));
data.add(new ArrayList<>(List.of("D", "E")));
// --- 访问元素 ---
// 获取第 1 行 (索引 0)
ArrayList<String> firstRow = data.get(0);
System.out.println("第一行: " + firstRow); // 输出: [A, B, C]
// 获取第 1 行的第 2 个元素 (索引 1)
String element = data.get(0).get(1);
System.out.println("第1行第2个元素是: " + element); // 输出: B
// --- 修改元素 ---
// 修改第 2 行 (索引 1) 的第 1 个元素 (索引 0)
data.get(1).set(0, "Z");
System.out.println("修改后的数据: " + data); // 输出: [[A, B, C], [Z, E]]
}
}
如何添加和删除行/列
添加行
直接使用外层 ArrayList 的 add() 方法即可。
// 假设 data 已经存在
ArrayList<String> newRow = new ArrayList<>();
newRow.add("X");
newRow.add("Y");
data.add(newRow); // 在末尾添加一行
data.add(0, newRow); // 在开头(索引0)插入一行
删除行
同样使用外层 ArrayList 的 remove() 方法。
// 假设 data 已经存在 data.remove(1); // 删除索引为 1 的那一行
添加列
“添加列”意味着要遍历每一行,然后在每一行的末尾(或指定位置)添加一个新元素。
// 假设 data 是 [[A, B, C], [D, E]]
String newColumnValue = "New";
for (ArrayList<String> row : data) {
row.add(newColumnValue); // 在每一行的末尾添加 "New"
}
// data 变成 [[A, B, C, New], [D, E, New]]
删除列
“删除列”同样需要遍历每一行,然后从每一行中删除指定索引的元素。

// 假设 data 是 [[A, B, C, New], [D, E, New]]
int columnIndexToRemove = 0; // 删除第 1 列
for (ArrayList<String> row : data) {
// 检查该行是否有足够的列,避免 IndexOutOfBoundsException
if (row.size() > columnIndexToRemove) {
row.remove(columnIndexToRemove);
}
}
// data 变成 [[B, C, New], [E, New]]
完整示例代码
这是一个综合了所有操作的完整示例。
import java.util.ArrayList;
import java.util.List;
public class TwoDArrayListOperations {
public static void main(String[] args) {
// 1. 初始化
ArrayList<ArrayList<String>> table = new ArrayList<>();
table.add(new ArrayList<>(List.of("Name", "Age", "City")));
table.add(new ArrayList<>(List.of("Alice", "30", "New York")));
table.add(new ArrayList<>(List.of("Bob", "24", "London")));
System.out.println("--- 初始表格 ---");
printTable(table);
// 2. 添加一行
ArrayList<String> newRow = new ArrayList<>(List.of("Charlie", "35", "Paris"));
table.add(newRow);
System.out.println("\n--- 添加新行后 ---");
printTable(table);
// 3. 修改一个元素
table.get(1).set(1, "31"); // Alice 的年龄从 30 改为 31
System.out.println("\n--- 修改年龄后 ---");
printTable(table);
// 4. 添加一列(国家)
System.out.println("\n--- 添加“国家”列 ---");
for (ArrayList<String> row : table) {
if (row == table.get(0)) {
row.add("Country"); // 添加表头
} else {
row.add("USA"); // 为其他行添加数据
}
}
printTable(table);
// 5. 删除一列(年龄)
System.out.println("\n--- 删除“年龄”列 ---");
for (ArrayList<String> row : table) {
row.remove(1); // 年龄列在索引 1
}
printTable(table);
// 6. 删除一行
table.remove(2); // 删除 Bob 的那一行
System.out.println("\n--- 删除 Bob 的行后 ---");
printTable(table);
}
// 辅助方法:用于美观地打印表格
public static void printTable(ArrayList<ArrayList<String>> table) {
for (ArrayList<String> row : table) {
System.out.println(row);
}
}
}
二维 ArrayList vs. 二维数组 (int[][])
| 特性 | ArrayList<ArrayList<T>> (二维动态列表) |
T[][] (二维数组) |
|---|---|---|
| 大小 | 动态,可以随时添加或删除行和列。 | 固定,创建时必须指定大小,改变大小需要创建新数组。 |
| 类型 | 可以存储任意对象类型 (Integer, String 等)。 |
可以存储基本类型 (int, char 等) 和对象类型。 |
| 性能 | 由于涉及到自动装箱/拆箱和动态扩容,访问速度相对较慢。 | 访问速度非常快,基于连续内存地址。 |
| 灵活性 | 非常高,每一行的长度都可以不同。 | 较低,所有行的长度必须相同(在声明时确定)。 |
| 内存 | 每个内部 ArrayList 都有自己的对象头,可能占用更多内存。 |
内存是连续的,结构紧凑。 |
-
何时使用二维
ArrayList?- 当你需要一个灵活的、可变大小的二维结构时。
- 当每一行的长度可能不同时。
- 当你主要进行增删操作,而对性能要求不是极端苛刻时。
-
何时使用二维数组 (
int[][])?- 当你知道数据的大小固定不变时。
- 当你需要最高性能的随机访问时(在游戏开发、科学计算中)。
- 当你处理的是基本数据类型(如
int,double)时,可以避免自动装箱的开销。
