二维数组本质上是一个“数组的数组”,对于 String 它就是一个一维数组,其中每一个元素又是一个 String 类型的一维数组,这非常适合用来表示类似表格、矩阵或具有行列结构的数据。

声明和初始化
创建二维 String 数组有几种常见的方式。
声明并指定大小,后赋值
这种方式先创建一个固定大小的二维数组,之后再为每个元素赋值。
// 1. 声明一个 3行 4列 的二维String数组 String[][] names = new String[3][4]; // 2. 为数组元素赋值 // 数组的索引从 0 开始 // names[行索引][列索引] names[0][0] = "Alice"; names[0][1] = "Bob"; names[0][2] = "Charlie"; names[0][3] = "David"; names[1][0] = "Eve"; names[1][1] = "Frank"; names[1][2] = "Grace"; names[1][3] = "Heidi"; names[2][0] = "Ivan"; names[2][1] = "Judy"; names[2][2] = "Mallory"; names[2][3] = "Oscar";
声明并初始化(静态初始化)
如果你在创建时就知道了所有元素,可以使用这种方式,代码更简洁。
// 使用花括号 {} 直接初始化数据
String[][] cities = {
{"Beijing", "Shanghai", "Guangzhou"},
{"New York", "Los Angeles", "Chicago"},
{"London", "Paris", "Berlin"}
};
// cities 是一个 3行 3列 的数组
声明“锯齿状”数组(不规则二维数组)
二维数组的每一行长度可以不同,这种数组也称为“锯齿状数组”(Jagged Array)。

// 每一行的列数都不同
String[][] jaggedArray = new String[3][]; // 先确定行数
jaggedArray[0] = new String[]{"A", "B"}; // 第1行有2列
jaggedArray[1] = new String[]{"C", "D", "E"}; // 第2行有3列
jaggedArray[2] = new String[]{"F"}; // 第3行有1列
访问和修改元素
通过行索引和列索引来访问或修改数组中的特定元素。
String[][] matrix = {
{"A", "B", "C"},
{"D", "E", "F"},
{"G", "H", "I"}
};
// 访问元素
String element = matrix[1][2]; // 获取第2行第3列的元素,结果是 "F"
System.out.println("Accessed element: " + element);
// 修改元素
matrix[0][0] = "X"; // 将第1行第1列的元素 "A" 修改为 "X"
System.out.println("Modified element: " + matrix[0][0]); // 输出 "X"
遍历二维数组
遍历二维数组通常需要使用嵌套的 for 循环:外层循环遍历行,内层循环遍历列。
使用传统的 for 循环
String[][] data = {
{"ID", "Name", "Age"},
{"1", "Alice", "30"},
{"2", "Bob", "25"}
};
System.out.println("--- Using traditional for loop ---");
for (int i = 0; i < data.length; i++) { // data.length 获取行数
for (int j = 0; j < data[i].length; j++) { // data[i].length 获取第 i 行的列数
System.out.print(data[i][j] + "\t"); // \t 是制表符,用于对齐
}
System.out.println(); // 每行结束后换行
}
输出:
--- Using traditional for loop ---
ID Name Age
1 Alice 30
2 Bob 25
使用增强型 for-each 循环
这种方式代码更简洁,适合只读取元素而不关心索引的场景。
String[][] teams = {
{"Team A", "Alice", "Bob"},
{"Team B", "Charlie", "David"}
};
System.out.println("\n--- Using enhanced for-each loop ---");
for (String[] row : teams) { // 遍历每一行,row 是一个一维 String 数组
for (String member : row) { // 遍历 row 中的每一个成员
System.out.print(member + "\t");
}
System.out.println();
}
输出:
--- Using enhanced for-each loop ---
Team A Alice Bob
Team B Charlie David
获取数组长度
array.length:获取二维数组的行数。array[i].length:获取第i行的列数。
String[][] testArray = {
{"a", "b"},
{"c", "d", "e"},
{"f"}
};
System.out.println("总行数: " + testArray.length); // 输出 3
System.out.println("第1行的列数: " + testArray[0].length); // 输出 2
System.out.println("第2行的列数: " + testArray[1].length); // 输出 3
System.out.println("第3行的列数: " + testArray[2].length); // 输出 1
完整示例
下面是一个完整的、可运行的示例,它演示了如何创建、初始化、遍历和搜索一个二维 String 数组。
public class TwoDStringArrayExample {
public static void main(String[] args) {
// 1. 初始化一个学生成绩表
String[][] studentGrades = {
{"张三", "语文", "90"},
{"李四", "数学", "85"},
{"王五", "英语", "92"},
{"张三", "数学", "88"},
{"赵六", "语文", "76"}
};
// 2. 遍历并打印成绩表
System.out.println("学生成绩表:");
System.out.println("----------------------------");
for (String[] record : studentGrades) {
System.out.printf("%-4s %-4s %s%n", record[0], record[1], record[2]);
// %-4s 表示左对齐,占4个字符宽度
// %n 是平台无关的换行符
}
System.out.println("----------------------------");
// 3. 搜索特定学生的所有成绩
String searchName = "张三";
System.out.println("\n查找 " + searchName + " 的所有成绩:");
boolean found = false;
for (String[] record : studentGrades) {
if (record[0].equals(searchName)) {
System.out.println("科目: " + record[1] + ", 成绩: " + record[2]);
found = true;
}
}
if (!found) {
System.out.println("未找到该学生的成绩。");
}
}
}
输出结果:
学生成绩表:
----------------------------
张三 语文 90
李四 数学 85
王五 英语 92
张三 数学 88
赵六 语文 76
----------------------------
查找 张三 的所有成绩:
科目: 语文, 成绩: 90
科目: 数学, 成绩: 88
二维数组 vs. ArrayList<ArrayList<String>>
在实际开发中,你可能会遇到使用 ArrayList 的替代方案,两者各有优劣:
| 特性 | String[][] (二维数组) |
ArrayList<ArrayList<String>> (嵌套列表) |
|---|---|---|
| 大小 | 固定,创建后大小不能改变。 | 动态,可以随时添加或删除元素。 |
| 性能 | 访问速度快,基于索引的随机访问是 O(1)。 | 添加/删除元素更灵活,但随机访问稍慢(尽管对于 ArrayList 来说也接近 O(1))。 |
| 内存 | 连续的内存块,内存占用可能更紧凑。 | 每个内部 ArrayList 都是一个对象,有额外的开销,内存可能不连续。 |
| 灵活性 | "锯齿状"数组可以实现,但语法稍显繁琐。 | 天然支持不规则结构,添加新行非常简单。 |
| 易用性 | 简单直接,适合数据量固定且明确的情况。 | API 更丰富(如 add(), remove(), size()),代码更灵活。 |
如何选择?
- 如果你知道数据的行数和列数是固定的(一个 3x3 的矩阵),并且追求最高的性能,使用
String[][]。 - 如果你需要动态地改变数组的大小(从文件中读取不确定行数的数据),或者数据结构本身就是不规则的,
ArrayList<ArrayList<String>>是更好的选择。
ArrayList<ArrayList<String>> 示例
import java.util.ArrayList;
public class ArrayListExample {
public static void main(String[] args) {
// 创建一个嵌套的 ArrayList
ArrayList<ArrayList<String>> dynamicData = new ArrayList<>();
// 添加第一行
ArrayList<String> row1 = new ArrayList<>();
row1.add("A");
row1.add("B");
dynamicData.add(row1);
// 添加第二行
ArrayList<String> row2 = new ArrayList<>();
row2.add("C");
dynamicData.add(row2);
// 添加第三行,并动态添加元素
ArrayList<String> row3 = new ArrayList<>();
dynamicData.add(row3);
dynamicData.get(2).add("D"); // 给第三行添加元素
dynamicData.get(2).add("E"); // 再添加一个
// 遍历
System.out.println("动态数据内容:");
for (ArrayList<String> row : dynamicData) {
for (String item : row) {
System.out.print(item + " ");
}
System.out.println();
}
// 动态添加新的一行
dynamicData.add(new ArrayList<>());
dynamicData.get(3).add("F");
System.out.println("\n添加新行后:");
System.out.println(dynamicData); // 直接打印 ArrayList
}
}
输出:
A B
C
D E
添加新行后:
[[A, B], [C], [D, E], [F]] 