CodeGym /课程 /JAVA 25 SELF /锯齿状数组(Jagged Arrays)

锯齿状数组(Jagged Arrays)

JAVA 25 SELF
第 7 级 , 课程 4
可用

1. 锯齿状数组与二维数组的区别

我们来到了一个很多人称为“锯齿状数组”的主题——英文称为 jagged arrays。与二维数组不同,锯齿状数组允许每一行的长度不同。可以把它想象成一个由多栋建筑组成的小区,每栋楼的房间数各不相同——一栋楼有 5 套,另一栋有 20 套,还有一栋只有一套。

锯齿状数组是一个数组,其中每个元素本身又是一个数组,而且这些内层数组(也称为“子数组”)的长度可以不同。

主要区别:

  • 二维数组中,每一“行”(以及每一“列”)的元素数量相同。 例如:int[][] grid = new int[3][5]; —— 我们始终有 3 行、每行 5 个元素。
  • 锯齿状数组中,每一行的长度都可以不同! 例如:int[][] jagged = new int[3][]; —— 然后我们再分别初始化每一行(子数组)。

直观示意如下:

二维数组 (3x3):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘

锯齿状数组(行长不同):
┌───┬───┐
│ 1 │ 2 │
├───┼───┼───┬───┐
│ 3 │ 4 │ 5 │ 6 │
├───┼───┴───┴───┘
│ 7 │
└───┘

2. 锯齿状数组的声明与初始化语法

声明锯齿状数组并不比之前的类型更可怕!别被两个方括号吓到:


int[][] jaggedArray = new int[3][];
锯齿状数组的声明:外层大小已指定,内层数组尚未创建

这意味着我们有一个包含 3 个元素的数组,而且每个元素本身也是一个int 数组。但此时内层数组还没有被创建!为更好地理解,我们来分步骤看看。

锯齿状数组的逐步初始化

步骤 1 —— 创建主(外层)数组:

int[][] jaggedArray = new int[3][];

现在我们有 3 行,但它们都还是 null

步骤 2 —— 创建并填充内层数组(子数组):
例如,第一行长度为 2,第二行为 4,第三行为 3

jaggedArray[0] = new int[2]; // 第一行 2 个元素
jaggedArray[1] = new int[4]; // 第二行 4 个元素
jaggedArray[2] = new int[3]; // 第三行 3 个元素

步骤 3 —— 赋值:
内层数组就是普通数组!例如:

jaggedArray[0][0] = 1;
jaggedArray[0][1] = 2;

jaggedArray[1][0] = 3;
jaggedArray[1][1] = 4;
jaggedArray[1][2] = 5;
jaggedArray[1][3] = 6;

jaggedArray[2][0] = 7;
jaggedArray[2][1] = 8;
jaggedArray[2][2] = 9;

锯齿状数组的简洁初始化

如果你已知所有值,也可以一次性创建并填充锯齿状数组:

int[][] jaggedArray = new int[][]
{
    new int[] { 1, 2 },
    new int[] { 3, 4, 5, 6 },
    new int[] { 7, 8, 9 }
};

或者更简洁一些,省略内层数组的类型:

int[][] jaggedArray = 
{
    { 1, 2 },
    { 3, 4, 5, 6 },
    { 7, 8, 9 }
};

3. 遍历与操作锯齿状数组

遍历锯齿状数组并不比遍历二维数组更难,只是现在外层循环按行遍历,内层循环遍历该行的元素(每行长度可能不同):

for (int i = 0; i < jaggedArray.length; i++) 
{
    System.out.println("行 " + i + ":");
    for (int j = 0; j < jaggedArray[i].length; j++) 
    {
        System.out.print(jaggedArray[i][j] + " ");
    }
    System.out.println();
}

屏幕上的结果:

行 0:
1 2 
行 1:
3 4 5 6 
行 2:
7 8 9 

你也可以使用 for-each,这样就不用关心索引:

for (int[] row : jaggedArray) 
{
    for (int value : row) 
    {
        System.out.print(value + " ");
    }
    System.out.println();
}

4. 锯齿状数组的典型使用场景

什么时候锯齿状数组比二维数组更合适?

  • 当你为每个用户存储不同数量的数据:例如各科成绩、购买记录、评论等。
  • 当你的数据呈现三角形或阶梯状结构(例如打印金字塔、帕斯卡三角形等)。
  • 当你希望节省内存:二维数组的各行长度固定,而锯齿状数组只分配所需的元素数量。

真实示例:学生成绩管理器

假设我们有三名学生,他们在数学不同作业上的成绩如下:

学生 成绩
0 5,4
1 3,4,4
2 5

声明这样的数组:

int[][] studentMarks = new int[3][];
studentMarks[0] = new int[] { 5, 4 };         // 第 1 个学生——2 个成绩
studentMarks[1] = new int[] { 3, 4, 4 };      // 第 2 个学生——3 个成绩
studentMarks[2] = new int[] { 5 };            // 第 3 个学生——1 个成绩

输出每位学生的成绩:

for (int i = 0; i < studentMarks.length; i++) 
{
    System.out.print("学生 " + i + ": ");
    for (int j = 0; j < studentMarks[i].length; j++) 
    {
        System.out.print(studentMarks[i][j] + " ");
    }
    System.out.println();
}

与其他类型一起使用锯齿状数组

锯齿状可以是任何类型的数组:字符串、更深层的数组的数组,甚至你自己的对象。

示例:字符串数组

String[][] groups = {
    { "伊万", "彼得" },
    { "玛丽娅", "阿列克谢", "谢尔盖" },
    { "瓦西里萨" }
};

5. 三维与多维数组

关于数组还有一个你可能已经想到的事实。如果可以创建二维数组,那么能否创建三维数组呢?

答案是可以,你可以创建任意维度的数组。这类数组称为多维数组

如何声明多维数组

只需用方括号写出所需的维度:

int[][][] cube = new int[2][3][4]; // 2 个“层”、3 行、4 列
cube[0][1][2] = 99;

这里我们有一个三维数组:

  • 第一维有 2 个元素,
  • 第二维有 3 个,
  • 第三维有 4 个。

这样的数组就像一个被顺序存放的数据“大立方体”。

遍历三维数组

访问元素时需要同时给出所有索引:

for (int i = 0; i < cube.length; i++) 
{
    for (int j = 0; j < cube[i].length; j++) 
    {
        for (int k = 0; k < cube[i][j].length; k++) 
        {
            System.out.print(cube[i][j][k] + " ");
        }
        System.out.println();
    }
    System.out.println("---");
}
  • 索引从 0 开始,这在 Java 中一贯如此。
  • 该数组共有 2 × 3 × 4 = 24 个元素。

多维数组的实际示例

  • 2D —— 表格、棋盘、图像。
  • 3D —— 计算机图形中的“小方块”、科研计算的数据(例如不同空间与时间点的温度)。
  • 4D 及以上 —— 很少使用,但会出现在高等数学、仿真、机器学习等领域。

6. 使用多维数组的常见错误

错误 1:数组越界

最常见的错误——尝试访问不存在的元素,例如:

int[][] arr = new int[2][3];
arr[2][0] = 5; // 错误!没有索引为 2 的行(只有 0 和 1)
arr[0][3] = 7; // 错误!没有索引为 3 的列(只有 0、1、2)

此类访问会抛出 ArrayIndexOutOfBoundsException。务必检查索引是否在允许范围内:从 0length - 1

错误 2:锯齿状数组中的行未初始化

如果只创建了锯齿状数组却忘了初始化内层数组,那么在访问时会得到 NullPointerException

int[][] jagged = new int[3][];
jagged[0][0] = 5; // 错误!jagged[0] == null

需要先创建内层数组:jagged[0] = new int[2];

错误 3:错误使用数组长度

容易把 matrix.length(行数)和 matrix[0].length(列数)混淆。复制、遍历、按列求和时尤其常见。

错误 4:假设所有行长度都相同

在锯齿状数组中,各行长度可能不同!如果你写 matrix[i][j],请确保 j < matrix[i].length

错误 5:索引次序混淆

有时会把行列顺序弄反:应该先行后列,即 matrix[row][column],不是相反!

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION