【Java核心知识】Java基础语法与相应面试技巧(五)

Java 数组核心知识点

上期面试题解答

上文链接:https://blog.csdn.net/weixin_73492487/article/details/146164026
1.方法重载和重写的区别?

答:重载是同类的同名不同参方法,重写是子类覆盖父类方法

2.如何解决递归栈溢出?

答:① 改用循环迭代 ② 增大栈空间(-Xss参数)③ 尾递归优化(伪实现)

3.以下代码输出什么?

public static void change(String s) {
    s = "new";
}
public static void main(String[] args) {
    String str = "old";
    change(str);
    System.out.println(str); // 输出"old"
}

4.可变参数方法能接收null吗?

答:可以,但需做空判断(nums == null)

1. 数组基础

1.1 定义与创建

定义: Java 语言中提供的数组是用来存储固定大小的同类型元素。你可以声明一个数组变量,如 nums[100] 来代替直接声明 100 个独立变量 num0,num1,…,num99

java数组本质上是一种对象(后文会具体解释)

创建:

// 一维数组
int[] arr1;          // 声明(推荐)
int arr2[];          // 声明(C风格,不推荐)
arr1 = new int[5];   // 动态初始化(先有声明,再有初始化,默认值0)

int[] arr2=new int[5];

// 二维数组
int[][] matrix = new int[3][4]; // 规则二维数组
int[][] jagged = new int[2][];   // 锯齿数组(每行长度不同)

1.2 初始化方式

类型 语法示例 特点
静态初始化 int[] arr = {1,2,3}; 直接指定元素值
动态初始化 String[] names = new String[3]; 指定长度,赋默认值
默认初始化 数组元素自动初始化 根据类型:int→0,对象→null

内存分析

  • 数组对象存储在堆内存中

数组是动态分配在堆内存中的对象。这与 Java 中其他类型的对象类似(例如实例化的类对象)。与局部变量或基本数据类型(如 int)不同,数组对象是通过 new 关键字创建的,并且分配的内存由 JVM 管理。

  • 数组变量(如arr1)是引用,存储堆中数组对象的地址
  • 二维数组本质是"数组的数组"(每个一维数组独立分配内存)

2. 核心特性

2.1 关键属性

int[] arr = new int[5];
System.out.println(arr.length); // 获取数组长度(非方法)

每个数组对象都有一个内建的属性 .length,这个属性表示数组的长度(即数组中元素的数量)。它具有作为对象的特性。

2.2 数组遍历

// 基础for循环
for(int i=0; i<arr.length; i++){...} 

// 增强for循环(只读)
for(int num : arr) {...}	//快捷写法:arrays.for

// 二维数组遍历
for(int[] row : matrix) {
    for(int num : row) {...}
}

2.3 常见问题

  1. 下标越界ArrayIndexOutOfBoundsException

    int[] arr = new int[3];
    arr[3] = 5; // 最大合法索引为2
    
  2. 空指针异常

    int[][] arr = new int[2][];
    System.out.println(arr[0][0]); // NullPointerException
    

    解释:arr 是一个长度为 2 的数组,每个元素应该是一个 int[] 数组(即二维数组的行)。但每个int[]子数组尚未初始化,所以它们的默认值为 null。

  3. 长度不可变:数组创建后长度固定


3. 内存机制详解

3.1 一维数组内存模型

栈内存        堆内存
arr  →  [0][0][0][0][0](以int[5]为实例)

3.2 二维数组内存结构

matrix → [ [0,0,0,0] ]  // matrix[0]指向第一个子数组
          [ [0,0,0,0] ]
          [ [0,0,0,0] ]		//matrix[3][4]

3.3 数组作为参数传递

public static void modify(int[] arr) {
    arr[0] = 100; // 修改会影响原始数组(引用传递,实际上也是值传递,但这个值指向对象的内存地址)
}

⚡ 高频面试题

  1. 数组声明方式是否正确?

    int[] arr1 = new int[3]{1,2,3}; // 错误(动态初始化不能指定元素)
    int[][] arr2 = new int[][3];     // 错误(必须指定行数)
    
  2. 以下代码输出什么?

    int[] arr = new int[5];
    System.out.println(arr[0]);      // 0(int默认值)
    
    String[] strs = new String[3];
    System.out.println(strs[1]);     // null(对象默认值)
    
  3. 如何实现数组深拷贝?(浅拷贝与深拷贝的区别,基本数据类型不分深拷贝与浅拷贝)

    int[] src = {1,2,3};
    int[] dest = Arrays.copyOf(src, src.length);	//使用 Arrays.copyOf() 进行深拷贝
    // 或
    System.arraycopy(src, 0, dest, 0, src.length);	//使用 System.arraycopy() 方法
    
  4. 数组与ArrayList的区别?(以后会学到ArrayList类,可以提前了解)

    • 数组固定长度,ArrayList动态扩容
    • 数组可直接存基本类型,ArrayList只能存对象
    • 数组性能更优(无自动装箱拆箱)

4. 数组与二维数组进阶

4.1 数组操作进阶

// 数组动态扩容
int[] arr = {1,2,3};
arr = Arrays.copyOf(arr, arr.length * 2); // 容量翻倍

// 数组判空技巧
if(array == null || array.length == 0) {
    // 处理空数组情况
}

// 不规则二维数组
int[][] matrix = new int[3][];
matrix[0] = new int[2];
matrix[1] = new int[4];
matrix[2] = new int[3];

4.2 内存结构图解

栈内存             堆内存
arrRef  →   [ [@1001, @1002],   // 二维数组引用
             [@1003, null ] ]
          
@1001: [1,2,3]   // 第一行数组
@1002: [4,5]     // 第二行数组
@1003: [6]       // 第三行数组

5. Arrays 工具类核心方法

5.1 常用API速查

方法 功能描述 示例
Arrays.toString(arr) 数组转字符串 "[1, 2, 3]"
Arrays.sort(arr) 数组排序(优化快排) 原数组被修改
Arrays.binarySearch(arr, key) 二分查找(需先排序) 返回索引或插入点负值
Arrays.copyOfRange() 复制指定范围数组 copyOfRange(src, 1, 4)
Arrays.fill(arr, val) 填充数组 fill(arr, -1)
Arrays.equals(arr1, arr2) 深度比较数组内容 多维数组也可比较

5.2 并行数组操作(JDK8+)

Arrays.parallelSort(largeArray);  // 并行排序(大数据量更快)

该方法用于并行排序数组中的元素。它通过多线程将排序任务划分为多个小任务,在多个处理器核心上并行执行,从而加快排序过程,尤其是在处理大数组时。
对于大数组或复杂排序操作,使用并行排序能够有效提升性能,因为它能够利用多核处理器的优势。


6. 稀疏数组(Sparse Array)

6.1 定义

稀疏数组(Sparse Array)是一种用于存储大部分元素为零(或空)的数组数据结构。通常,稀疏数组用于节省内存空间,尤其是在处理二维或更高维度的大数组时。如果一个数组中的大部分元素都是相同的、重复的(例如零或 null),使用稀疏数组可以仅存储非零的元素和它们的位置信息,从而大大节省空间。

6.2 理解

存储大量重复默认值(通常为0)的二维数组时,压缩存储:

原始数组(5x5):
0 0 0 0 0
0 2 0 0 0
0 0 3 0 0
0 0 0 0 0
0 0 0 0 0

稀疏数组:
(行数 列数 非零元素个数)
5	5 	2   // 行数 列数 非零元素个数
(行 列  值)
1 	1 	2   // 行 列 值
2 	2 	3

6.3 转换实现

代码:

public class SparseArray {
    // 定义一个稀疏数组,存储稀疏数组的三元组
    private int[][] sparseArray;

    // 构造函数,接受原始数组的行数、列数和非零元素的个数
    public SparseArray(int rows, int cols, int nonZeroCount) {
        sparseArray = new int[nonZeroCount + 1][3]; // 多一行用于存储数组的元信息
        sparseArray[0][0] = rows;  // 存储原数组的行数
        sparseArray[0][1] = cols;  // 存储原数组的列数
        sparseArray[0][2] = nonZeroCount; // 存储非零元素的个数
    }

    // 设置稀疏数组中的值
    public void set(int index, int row, int col, int value) {	//index对应第几个非零元素
        sparseArray[index + 1][0] = row;
        sparseArray[index + 1][1] = col;
        sparseArray[index + 1][2] = value;
    }

    // 获取稀疏数组中的值
    public int[] get(int index) {
        return sparseArray[index + 1];
    }

    // 打印稀疏数组
    public void print() {
        for (int i = 0; i < sparseArray.length; i++) {
            System.out.println(sparseArray[i][0] + "\t" + sparseArray[i][1] + "\t" + sparseArray[i][2]);
        }
    }

    // 主函数:将一个普通的二维数组转化为稀疏数组
    public static SparseArray toSparseArray(int[][] matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        int nonZeroCount = 0;

        // 计算非零元素个数
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (matrix[i][j] != 0) {
                    nonZeroCount++;
                }
            }
        }

        SparseArray sparseArray = new SparseArray(rows, cols, nonZeroCount);

        // 将非零元素存入稀疏数组
        int index = 0;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (matrix[i][j] != 0) {
                    sparseArray.set(index, i, j, matrix[i][j]);
                    index++;
                }
            }
        }

        return sparseArray;
    }

    public static void main(String[] args) {
        // 原始二维数组
        int[][] matrix = {
            {0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0},
            {0, 3, 0, 0, 0},
            {0, 0, 0, 0, 0},
            {0, 0, 0, 4, 0}
        };

        SparseArray sparseArray = SparseArray.toSparseArray(matrix);
        sparseArray.print();
    }
}

输出结果:

5	5	2
2	1	3
4	3	4

6.4 稀疏数组的优缺点和应用场景

1.优点:

  • 节省内存:只存储非零元素,适合大多数元素为零的情况。
  • 提高效率 :减少了存储空间的浪费,尤其在存储稀疏数据时,可以显著降低内存占用。
    2.缺点:
  • 访问效率较低:因为稀疏数组要通过行列位置查找数据,相较于直接访问数组的元素,访问时的复杂度较高。
  • 需要额外的管理结构:需要专门的存储结构来维护稀疏数组,这也会带来一定的开销。
    3.应用场景
  • 图像处理:图像中大多数像素值为零(黑色或透明),使用稀疏数组能够有效地存储图像数据。
  • 棋盘、地图等:例如,在棋盘游戏、路径寻找等场景中,大多数位置为空,稀疏数组可以节省空间。
  • 图的表示:在图的邻接矩阵表示中,很多元素为零(即无边),可以使用稀疏数组来存储图

下期:排序算法

你可能感兴趣的:(java,面试,开发语言)