Java多重数组使用及说明

 更新时间:2026年01月31日 09:09:26   作者:心之语歌  
本文介绍了Java中多维数组的定义、声明、初始化、访问、遍历以及内存结构,文章还讨论了Java多维数组与C/C++的区别,以及如何处理锯齿数组和深拷贝,此外,还提到了数组与链表的区别,包括它们的内存布局、操作效率和适用场景

概念

在 Java 中,“多重数组”通常指的是多维数组(Multidimensional Arrays),最常见的是二维数组(如矩阵),但也可以有三维、四维等。

多重数组概念

Java 的多重数组本质上是 “数组的数组”(array of arrays),即:

  • 一维数组:int[] arr
  • 二维数组:int[][] matrix → 每个元素是一个 int[]
  • 三维数组:int[][][] cube → 每个元素是一个 int[][]

Java 的多维数组不要求每行长度相同(称为“锯齿数组”或“不规则数组”)。

声明与初始化

1. 声明

int[][] matrix;          // 声明一个二维整型数组
String[][][] data;       // 三维字符串数组

2. 初始化方式

方式一:直接字面量初始化(推荐用于小数据)

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

方式二:指定大小(规则矩形)

int rows = 3, cols = 4;
int[][] mat = new int[rows][cols];  // 3x4 的全 0 矩阵

方式三:锯齿数组(Jagged Array)— 每行长度不同

int[][] jagged = new int[3][];      // 3 行,列数未定
jagged[0] = new int[2];             // 第0行有2列
jagged[1] = new int[5];             // 第1行有5列
jagged[2] = new int[1];             // 第2行有1列

注意:new int[3][4] 是规则数组;new int[3][] 是锯齿数组(需手动初始化每行)。

访问与修改元素

int value = matrix[i][j];     // 读取第 i 行第 j 列
matrix[i][j] = 100;           // 修改

边界检查:Java 会在运行时自动检查下标是否越界(抛出 ArrayIndexOutOfBoundsException)。

遍历多重数组

1. 普通 for 循环(知道行列数)

for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        System.out.print(matrix[i][j] + " ");
    }
    System.out.println();
}
  • matrix.length → 行数
  • matrix[i].length → 第 i 行的列数(支持锯齿数组!)

2. 增强 for 循环(for-each,简洁安全)

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

推荐:优先使用 for-each,避免下标错误,尤其处理锯齿数组时更安全。

内存结构(重要理解)

Java 的二维数组不是一块连续内存(不像 C/C++),而是:

  • 一个一维数组,每个元素指向另一个一维数组
  • 所以可以每行长度不同(锯齿)
matrix → [ ref0, ref1, ref2 ]
           ↓     ↓     ↓
         [1,2] [3,4,5] [6]

优势:灵活;劣势:缓存局部性较差(相比连续内存)

常见使用场景(面试高频)

场景示例
矩阵操作旋转矩阵、螺旋遍历、对角线遍历
动态规划二维 DP 表(如 LCS、编辑距离)
图的邻接矩阵graph[i][j] == 1 表示 i→j 有边
棋盘/地图模拟迷宫、岛屿数量、生命游戏
分组存储每行代表一类数据

实用工具方法

1. 获取行列数

int rows = matrix.length;
int cols = matrix[0].length;  // 注意:仅当至少有一行且非空时安全!

安全写法(处理空数组):

if (matrix == null || matrix.length == 0) return;
int rows = matrix.length;
int cols = matrix[0].length; // 此时可安全访问

2. 深拷贝二维数组(避免引用共享)

int[][] deep = new int[original.length][];
for (int i = 0; i < original.length; i++) {
    deep[i] = original[i].clone(); // 克隆每一行
}
类型含义特点
浅拷贝(Shallow Copy)复制对象的引用,不复制内部对象新旧数组共享子对象
深拷贝(Deep Copy)递归复制所有层级的对象完全独立,互不影响
数组类型浅拷贝是否安全?如何实现深拷贝
int[], double[] 等基本类型✅ 安全(值拷贝)arr.clone() 即可
String[]⚠️ 表面安全(String 不可变)通常 clone() 足够
int[][](二维基本类型)❌ 不安全循环 + row.clone()
Object[](含可变对象)❌ 不安全需手动深拷贝每个元素(可能递归)

注意:String 虽是引用类型,但不可变(immutable),所以浅拷贝通常不会出问题。但如果是 StringBuilder[],就必须深拷贝!

误区正确理解
“clone() 就是深拷贝”❌ 默认是浅拷贝,除非重写 clone() 方法
“Arrays.copyOf() 是深拷贝”❌ 对多维数组仍是浅拷贝
“基本类型数组不需要深拷贝”✅ 正确!因为存的是值,不是引用

常见陷阱 & 注意事项

问题说明
空指针异常matrix 为 null,或某行为 null(锯齿数组未初始化)
列数不一致误以为所有行长度相同,直接用 matrix[0].length 遍历所有行
浅拷贝问题直接赋值导致修改副本影响原数组
内存浪费用二维数组存稀疏矩阵(此时应考虑 Map<Pair, Value> 或稀疏表示)

数组跟链表

数组(Array)和链表(Linked List)是两种最基础、最重要的线性数据结构。它们在内存布局、操作效率、适用场景上有本质区别。掌握它们的差异,是算法设计和系统优化的关键。

特性数组(Array)链表(Linked List)
内存布局连续内存块非连续,靠指针连接
随机访问✅ O(1)(通过下标)❌ O(n)(必须遍历)
插入/删除(中间)❌ O(n)(需移动元素)✅ O(1)(已知节点时)
插入/删除(头部)❌ O(n)(除非用特殊技巧)✅ O(1)
插入/删除(尾部)✅ O(1)(动态数组均摊)✅ O(1)(若有 tail 指针)
空间开销仅数据本身每个节点额外存指针(如 next)
缓存友好性✅ 高(局部性好)❌ 低(内存跳跃)
大小固定?静态数组固定;动态数组可扩容动态伸缩,无需预分配
场景推荐结构原因
需要频繁随机访问(如排序、DP)✅ 数组O(1) 访问
频繁在头部/中间插入删除✅ 链表O(1) 修改指针
元素数量固定或可预估✅ 数组内存紧凑,性能高
实现栈(只操作尾部)✅ 数组 or 链表两者都 O(1)
实现队列(首尾操作)✅ 链表 or 循环数组链表天然支持;数组需循环缓冲
内存敏感(小对象大量存储)✅ 数组链表指针开销大
需要缓存友好(高性能计算)✅ 数组连续内存,预取高效

扩展:高维数组(了解即可)

// 三维数组:3 层,每层 4 行,每行 5 列
int[][][] cube = new int[3][4][5];

// 访问
cube[0][1][2] = 10;

// 遍历
for (int[][] layer : cube) {
    for (int[] row : layer) {
        for (int val : row) {
            // ...
        }
    }
}

实际开发中,三维以上数组较少见,通常用对象封装更清晰。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中的

    Java中的"goto"语句妙用

    这篇文章主要介绍了Java中的"goto"语句妙用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • 详解Java爬虫利器Jsoup

    详解Java爬虫利器Jsoup

    Jsoup是一款Java语言开发的HTML解析器,用于解析HTML文档以及对HTML文档进行操作,处理等,本文就将详细给大家介绍一下Java中的爬虫利器Jsoup,感兴趣的同学可以参考一下
    2023-06-06
  • 详解hashCode()和equals()的本质区别和联系

    详解hashCode()和equals()的本质区别和联系

    这篇文章主要介绍了详解hashCode()和equals()的本质区别和联系,本文先对两种方法作了介绍,然后对二者联系进行分析,具有一定参考价值,需要的朋友可以了解下。
    2017-09-09
  • JAVA设计模式之解释器模式详解

    JAVA设计模式之解释器模式详解

    这篇文章主要介绍了JAVA设计模式之解释器模式详解,解释器模式是类的行为模式,给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器,需要的朋友可以参考下
    2015-04-04
  • IDEA如何开启并配置services窗口

    IDEA如何开启并配置services窗口

    在使用IntelliJ IDEA时,可能会遇到Services窗口不自动弹出的情况,本文介绍了如何手动开启Services窗口的简单步骤,首先,通过点击菜单栏中的“视图”->“工具窗口”->“服务”,或使用快捷键Alt+F8(注意快捷键可能存在冲突)来打开Services窗口
    2024-10-10
  • MyBatis通用Mapper实现原理及相关内容

    MyBatis通用Mapper实现原理及相关内容

    今天小编就为大家分享一篇关于MyBatis通用Mapper实现原理及相关内容,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • SpringBoot中的@FeignClient注解使用

    SpringBoot中的@FeignClient注解使用

    文章主要介绍了SpringCloud中的@FeignClient注解的使用及其参数详解,包括value/name、url、path、configuration、fallback/fallbackFactory、contextId等,通过@FeignClient注解,可以方便地声明一个REST客户端,并定义与目标服务通信的接口
    2024-11-11
  • 如何使用MybatisPlus的SQL注入器提升批量插入性能

    如何使用MybatisPlus的SQL注入器提升批量插入性能

    本文给大家介绍如何使用MybatisPlus的SQL注入器提升批量插入性能,以实战视角讲述如何利用该特性提升MybatisPlus 的批量插入性能,感兴趣的朋友跟随小编一起看看吧
    2024-05-05
  • Spring实例化Bean的三种方式详解

    Spring实例化Bean的三种方式详解

    在Spring中,构建应用程序主干并且由Spring IoC容器管理的对象被称为beans,bean是一个由Spring容器实例化,组装和管理的对象,本文给大家介绍了Spring实例化Bean的三种方式,并有详细的代码示例供大家参考,需要的朋友可以参考下
    2025-08-08
  • SpringMVC 如何使用thymeleaf 进行数据展示

    SpringMVC 如何使用thymeleaf 进行数据展示

    thymeleaf是前端的视图解析器,可以用于html页面上变量的渲染,如何来使用thymeleaf,下面通过本文给大家介绍SpringMVC 如何使用thymeleaf进行数据展示,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-05-05

最新评论