在 Java 中,没有真正的“二维 ArrayList”这种原生数据类型,我们通常使用 ArrayList 的 ArrayList,也就是一个 ArrayList 里面存放的是其他的 ArrayList,每个内部的 ArrayList 代表一行,而内部的元素则代表该行中的列。

声明和初始化
创建一个二维 ArrayList 通常有两种方式:动态扩容和指定初始大小。
动态扩容(最常用)
这种方式从一个空的 ArrayList 开始,然后根据需要动态添加行和列。
// 1. 声明一个外部的 ArrayList,它将存储 ArrayList<Integer> 类型的对象(即行) ArrayList<ArrayList<Integer>> twoDArrayList = new ArrayList<>(); // 2. 添加第一行 ArrayList<Integer> row1 = new ArrayList<>(); row1.add(1); row1.add(2); row1.add(3); twoDArrayList.add(row1); // 3. 添加第二行 ArrayList<Integer> row2 = new ArrayList<>(); row2.add(4); row2.add(5); row2.add(6); twoDArrayList.add(row2); // 4. 添加第三行(这一行只有两个元素) ArrayList<Integer> row3 = new ArrayList<>(); row3.add(7); row3.add(8); twoDArrayList.add(row3); // twoDArrayList 的结构是: // [ // [1, 2, 3], // [4, 5, 6], // [7, 8] // ]
预定义行数和列数
如果你知道最终的行数和列数,可以先创建一个固定大小的框架,然后再填充数据,这类似于传统二维数组的初始化。
int rows = 3;
int cols = 4;
// 1. 创建一个包含 'rows' 个空 ArrayList 的外部 ArrayList
ArrayList<ArrayList<String>> matrix = new ArrayList<>(rows);
// 2. 使用循环为每一行创建一个新的 ArrayList 并添加到外部 ArrayList 中
for (int i = 0; i < rows; i++) {
// 为每一行创建一个内部 ArrayList
ArrayList<String> row = new ArrayList<>(cols); // 预分配列数可以提高性能
matrix.add(row);
// 向这一行中添加元素
for (int j = 0; j < cols; j++) {
row.add("Element-" + i + "-" + j);
}
}
// matrix 的结构是:
// [
// [Element-0-0, Element-0-1, Element-0-2, Element-0-3],
// [Element-1-0, Element-1-1, Element-1-2, Element-1-3],
// [Element-2-0, Element-2-1, Element-2-2, Element-2-3]
// ]
访问元素
访问元素使用 get() 方法进行两次嵌套。

outerList.get(i)获取第i行(这是一个内部的ArrayList)。- 然后对这个内部列表调用
.get(j)获取第j列的元素。
注意: Java 的索引从 0 开始。
// 假设我们使用方式一创建的 twoDArrayList
int element = twoDArrayList.get(0).get(1); // 获取第一行第二列的元素 -> 2
System.out.println("元素是: " + element); // 输出: 元素是: 2
String element2 = matrix.get(1).get(2); // 获取第二行第三列的元素 -> "Element-1-2"
System.out.println("元素是: " + element2); // 输出: 元素是: Element-1-2
安全访问: 像传统数组一样,访问前最好检查索引是否越界,以避免 IndexOutOfBoundsException。
int i = 2;
int j = 1;
if (i < twoDArrayList.size() && j < twoDArrayList.get(i).size()) {
System.out.println("安全访问的元素: " + twoDArrayList.get(i).get(j));
} else {
System.out.println("索引越界!");
}
修改元素
修改元素同样使用两次 get() 来定位,然后使用 set() 方法。
// 将 twoDArrayList 中第一行第二列的元素从 2 修改为 99
twoDArrayList.get(0).set(1, 99);
// 修改后,twoDArrayList 的结构变为:
// [
// [1, 99, 3],
// [4, 5, 6],
// [7, 8]
// ]
System.out.println("修改后的元素: " + twoDArrayList.get(0).get(1)); // 输出: 修改后的元素: 99
遍历二维 ArrayList
有几种常见的遍历方式。

使用传统的 for 循环
System.out.println("--- 使用 for 循环遍历 ---");
for (int i = 0; i < twoDArrayList.size(); i++) {
// 获取当前行
ArrayList<Integer> currentRow = twoDArrayList.get(i);
for (int j = 0; j < currentRow.size(); j++) {
System.out.print(currentRow.get(j) + " ");
}
System.out.println(); // 换行
}
使用增强 for 循环(更简洁、推荐)
这种方式代码更易读。
System.out.println("\n--- 使用增强 for 循环遍历 ---");
for (ArrayList<Integer> row : twoDArrayList) {
for (Integer element : row) {
System.out.print(element + " ");
}
System.out.println();
}
添加和删除行/列
添加行
直接使用外层 ArrayList 的 add() 方法。
// 在末尾添加一行 ArrayList<Integer> newRow = new ArrayList<>(); newRow.add(10); newRow.add(11); twoDArrayList.add(newRow); // 在指定位置插入一行(在第1行和第2行之间插入) ArrayList<Integer> anotherRow = new ArrayList<>(); anotherRow.add(20); anotherRow.add(21); twoDArrayList.add(1, anotherRow); // 在索引 1 的位置插入
删除行
直接使用外层 ArrayList 的 remove() 方法。
// 删除最后一行 twoDArrayList.remove(twoDArrayList.size() - 1); // 删除指定索引的行(删除索引为 1 的行) twoDArrayList.remove(1);
添加/删除列
“列”的概念不那么直接,因为每一行的长度可以不同。
- 添加列: 需要遍历每一行,并在每一行的末尾(或指定位置)添加一个元素。
- 删除列: 同样需要遍历每一行,并从每一行中删除指定位置的元素。
// 在每一行的末尾添加一个新列,值为 0
for (ArrayList<Integer> row : twoDArrayList) {
row.add(0);
}
// 删除每一行的最后一列(如果行不为空)
for (ArrayList<Integer> row : twoDArrayList) {
if (!row.isEmpty()) {
row.remove(row.size() - 1);
}
}
与传统二维数组 (int[][]) 的对比
| 特性 | ArrayList<ArrayList<T>> |
T[][] (传统二维数组) |
|---|---|---|
| 大小 | 动态,行和列的数量都可以随时改变。 | 固定,必须在创建时指定大小,之后不能改变。 |
| 内存 | 灵活,但可能有少量额外的开销(每个内部 ArrayList 对象的开销)。 |
连续的内存块,效率高,但浪费内存(如果某些行很短)。 |
| 性能 | 添加/删除元素非常快,随机访问比数组稍慢(因为涉及两次方法调用)。 | 随机访问极快(O(1)),大小固定,不灵活。 |
| 数据类型 | 可以存放任意对象类型(如 Integer, String)。 |
可以存放基本类型(int, char)或对象。 |
| 不规则性 | 天然支持“锯齿数组”(每行长度不同)。 | 不支持,所有行的长度必须相同。 |
-
何时使用
ArrayList<ArrayList<T>>?- 当你需要一个动态大小的二维结构时。
- 当你的数据是不规则的(每行的列数不同)时。
- 当你频繁需要添加或删除行/列时。
-
何时使用传统二维数组
T[][]?- 当你在编译时就知道确切的大小,并且它不会改变时。
- 当你需要最高性能,尤其是在进行大量随机访问时。
- 当处理基本类型数据(如
int,double)时,使用数组更节省内存(因为ArrayList只能存放对象)。
