一、数组的概述
定义与特点
数组是相同类型数据元素的有序集合,存储在连续内存空间中。其特点包括:
元素类型相同,索引从0开始。
长度固定,初始化后不可修改。
支持快速随机访问(时间复杂度O(1))。
分类
一维数组:线性结构,如 int arr = {1, 2, 3};。
二维数组:可视为“数组的数组”,常用于矩阵或表格,如 int matrix = new int33;。
多维数组:更高维度的扩展,如三维数组。
二、一维数组的初始化、遍历与默认值
初始化
静态初始化:直接指定元素值,如 int arr = {1, 2, 3};。
动态初始化:指定长度后赋值,如 int arr = new int5;(默认值为0)。
遍历方式
循环遍历:通过索引访问,如 for (int i = 0; i < arr.length; i++)。
增强型循环(如Java的for-each):for (int num : arr)。
迭代器/指针遍历:适用于特定语言(如C++的指针)。
默认初始化值
基本类型:数值型为0,布尔型为false,字符型为\u0000^。
引用类型:默认值为null(如String)。
三、一维数组的内存解析
内存分配
栈内存:存储数组引用变量(如arr)。
堆内存:存储实际数组元素,连续分配。
示例
java
int arr = new int3;
// 栈中存引用arr,堆中分配连续12字节(int占4字节)
修改元素时,直接操作堆内存地址。
四、二维数组的初始化、遍历与默认值
初始化
静态初始化:直接指定内层数组,如 int matrix = {{1, 2}, {3, 4}};。
动态初始化:指定外层长度,内层可动态分配,如 int matrix = new int2;。
遍历方式
嵌套循环:外层循环行,内层循环列,如:
java
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrixi.length; j++) {
System.out.print(matrixij);
}
}
。
默认初始化值
外层元素:若未初始化内层数组,默认值为null。
内层元素:同于一维数组(如数值型为0)。
五、二维数组的内存解析
内存结构
二维数组是外层数组引用内层数组的结构,如 matrix0 指向第一个一维数组。
内存中仍为连续分配,但外层数组元素存储的是内层数组的地址。
示例
java
int matrix = new int23;
// 外层数组长度2,每个元素指向一个长度为3的一维数组
六、常见算法操作
特征值计算
特征值用于矩阵分析,通过求解方程 Ax = λx 获得。
应用场景:主成分分析(PCA)、图像处理。
数组赋值与复制
赋值:直接传递引用(如 arr2 = arr1,修改会影响原数组)。
复制:创建新对象(如 System.arraycopy() 或循环赋值)。
反转、扩容与缩容
反转:首尾交换元素(时间复杂度O(n))。
扩容/缩容:创建新数组并复制数据(如Java的Arrays.copyOf())。
查找与排序
查找:顺序查找(O(n))、二分查找(O(log n))。
冒泡排序:相邻元素比较交换,时间复杂度O(n²)。
快速排序:分治策略,平均时间复杂度O(n log n)。
七、Arrays工具类与常见异常
Arrays工具类
常用方法:
sort():排序
binarySearch():二分查找
toString():数组转字符串。
常见异常
越界异常:访问不存在的索引(如arr5,数组长度为5时)。
空指针异常:操作未初始化的数组(如int arr; arr0 = 1;)。
Java数组常见及进阶面试题解析
一、常见基础面试题
数组声明与初始化
题目:如何声明并初始化一维/二维数组?
解析:
一维数组:int arr = new int5(动态初始化)或 int arr = {1, 2, 3}(静态初始化)。
二维数组:int matrix = new int34,内存中按行优先存储,可通过行和列索引访问。
注意点:数组长度固定,初始化后不可修改。
数组遍历与操作
题目:如何遍历数组并打印元素?
解析:
使用普通for循环(需控制索引范围)或增强型for循环(仅需访问元素值)。
示例代码:
java
int arr = {1, 2, 3};
for (int num : arr) {
System.out.print(num + " ");
}
陷阱:避免数组越界异常(ArrayIndexOutOfBoundsException),需确保索引在0, length-1范围内。
数组排序与查找
题目:如何对数组排序?如何实现二分查找?
解析:
排序:使用Arrays.sort(arr)(默认升序,支持自定义比较器)。
二分查找:需数组有序,通过Arrays.binarySearch(arr, key)实现,时间复杂度为O(log n)。
进阶:手动实现二分查找时,注意中间值的计算和边界条件。
数组异常与内存管理
题目:如何避免数组越界异常?二维数组的内存地址如何计算?
解析:
越界处理:使用Arrays.copyOf()扩展数组,避免直接操作原数组。
内存地址:若二维数组A1010中元素A20地址为560,则A10的地址为560 - 10*4 = 520(每个元素占4字节)。
数组与集合的区别
题目:数组与ArrayList有何区别?
解析:
数组:固定长度,支持基本数据类型和对象;内存连续,访问速度快。
ArrayList:动态扩容(默认增长50%),仅存储对象;提供更多操作方法(如add()、remove())。
二、进阶面试题
动态数组实现原理
题目:如何手动实现动态数组?
解析:
底层仍为数组,当容量不足时创建新数组并拷贝元素(类似ArrayList的扩容机制)。
示例步骤:初始化默认容量→添加元素→容量不足时扩容(如翻倍)→拷贝旧数组至新数组。
数组性能优化
题目:处理大数组时如何优化内存和性能?
解析:
内存优化:优先使用基本类型数组(如int而非Integer),减少对象开销。
算法优化:避免嵌套循环(时间复杂度O(n²)),使用双指针或哈希表优化查找。
多维数组应用场景
题目:哪些场景适合使用多维数组?
解析:
矩阵运算、棋盘类游戏(如五子棋)、图像处理(像素矩阵)等。
注意:Java中多维数组本质是“数组的数组”,内存非连续,可能影响缓存命中率。
系统设计中的数组应用
题目:如何用数组实现队列或栈?
解析:
循环队列:使用数组和两个指针(头、尾),通过取模运算实现循环存储。
栈:用数组模拟后进先出(LIFO)结构,需记录栈顶指针。
算法实战题
题目:寻找数组中的重复元素或缺失值。
解析:
重复元素:使用哈希表(时间复杂度O(n))或原地交换法(适用于元素值在0, n-1范围内)。
缺失值:利用数学公式(总和差值)或异或运算(XOR)。
三、高频易错题示例
非法数组引用
题目:以下哪种数组引用是非法的?
java
int a34;
a02*1 // A
a13 // B
a4-20 // C
a022 // D
答案:D(索引越界)。
折半查找轨迹
题目:在有序数组{2,4,6,8,10,12,14,16,18,20,22}中查找16,比较轨迹是?
答案:12 → 18 → 14 → 16(通过中间值逐步缩小范围)。
四、总结与建议
基础重点:掌握数组的声明、遍历、排序及与集合的区别。
进阶方向:深入理解内存布局、动态数组实现及性能优化策略。
实战准备:结合LeetCode高频题(如双指针、滑动窗口)强化算法能力。