【Java SE 基础学习打卡】36 数组的常见操作

目录

  • 前言
  • 一、数组元素的查找:线性查找(翻收纳盒找东西)
    • [1.1 生活化类比](#1.1 生活化类比)
    • [1.2 核心原理](#1.2 核心原理)
    • [1.3 代码示例(找指定成绩,返回索引)](#1.3 代码示例(找指定成绩,返回索引))
    • [1.4 线性查找的特点(新手记)](#1.4 线性查找的特点(新手记))
  • [二、数组元素的排序:冒泡排序(让大元素 "浮" 到末尾)](#二、数组元素的排序:冒泡排序(让大元素 “浮” 到末尾))
    • [2.1 生活化类比](#2.1 生活化类比)
    • [2.2 核心原理(升序排序)](#2.2 核心原理(升序排序))
    • [2.3 基础代码实现(冒泡排序升序)](#2.3 基础代码实现(冒泡排序升序))
    • [2.4 冒泡排序优化(避免无效循环)](#2.4 冒泡排序优化(避免无效循环))
  • [三、数组元素的增删改:长度固定也能 "灵活操作"](#三、数组元素的增删改:长度固定也能 “灵活操作”)
    • [3.1 最简单:元素修改(直接替换)](#3.1 最简单:元素修改(直接替换))
    • [3.2 数组新增元素(扩容思路)](#3.2 数组新增元素(扩容思路))
    • [3.3 数组删除元素(前移覆盖)](#3.3 数组删除元素(前移覆盖))
    • [3.4 关键提醒:数组增删的本质](#3.4 关键提醒:数组增删的本质)
  • [四、数组的拷贝:System.arraycopy ()(备份收纳盒)](#四、数组的拷贝:System.arraycopy ()(备份收纳盒))
    • [4.1 方法参数详解(新手记牢)](#4.1 方法参数详解(新手记牢))
    • [4.2 代码示例(拷贝全部 / 部分元素)](#4.2 代码示例(拷贝全部 / 部分元素))
    • [4.3 应用场景(新手常用)](#4.3 应用场景(新手常用))
    • [4.4 避坑提醒:数组拷贝的边界校验](#4.4 避坑提醒:数组拷贝的边界校验)
  • [五、新手必避的 5 个 "操作致命坑"](#五、新手必避的 5 个 “操作致命坑”)
    • [5.1 坑 1:冒泡排序内层循环条件写错](#5.1 坑 1:冒泡排序内层循环条件写错)
    • [5.2 坑 2:新增 / 删除元素时直接修改原数组](#5.2 坑 2:新增 / 删除元素时直接修改原数组)
    • [5.3 坑 3:线性查找返回布尔值,不返回索引](#5.3 坑 3:线性查找返回布尔值,不返回索引)
    • [5.4 坑 4:数组拷贝时目标数组未初始化](#5.4 坑 4:数组拷贝时目标数组未初始化)
    • [5.5 坑 5:删除元素时忽略索引合法性校验](#5.5 坑 5:删除元素时忽略索引合法性校验)
  • 总结

前言

上一节咱们掌握了数组元素的访问和遍历,现在要落地到实际编程场景:比如从成绩数组里找有没有 95 分的学生(查找)、把成绩按从低到高排好序(排序)、给数组新增一个学生成绩(新增)、删掉无效的成绩(删除)、备份一份成绩数组(拷贝)。

新手容易觉得 "数组长度固定" 就没法增删元素,也容易被冒泡排序的 "相邻交换" 绕晕,这一节咱们就用 "整理收纳盒里的东西" 作为类比,把数组的查找、排序、增删改、拷贝这 4 个核心操作讲透,每个操作都给可运行的代码,还会讲优化思路和避坑点,让新手能直接落地使用!

一、数组元素的查找:线性查找(翻收纳盒找东西)

数组查找是 "从一堆元素里找指定值",最基础的是线性查找(也叫顺序查找),适合无序数组,原理简单易懂。

1.1 生活化类比

想从装满成绩纸条的收纳盒里找 "95 分" 的纸条:

  • 从第一个纸条开始,逐个打开看;

  • 找到就停下,没找到就翻到最后一个;

  • 核心:按顺序遍历,逐个匹配,适合杂乱的收纳盒(无序数组)。

1.2 核心原理

遍历数组的每个元素,和目标值逐一比较:

  • 匹配成功:返回该元素的索引;

  • 遍历结束仍未匹配:返回 - 1(约定俗成的 "未找到" 标识)。

1.3 代码示例(找指定成绩,返回索引)

java 复制代码
public class ArraySearch {
    // 线性查找方法:arr是数组,target是要找的目标值
    public static int linearSearch(int[] arr, int target) {
        // 遍历数组,逐个匹配
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == target) {
                return i; // 找到,返回索引
            }
        }
        return -1; // 没找到,返回-1
    }

    public static void main(String[] args) {
        int[] scores = {90, 85, 95, 88, 92};
        int target = 95;
        int index = linearSearch(scores, target);
        
        if (index != -1) {
            System.out.println("找到" + target + "分,索引是:" + index);
        } else {
            System.out.println("没找到" + target + "分");
        }
    }
}

执行结果:

java 复制代码
找到95分,索引是:2

1.4 线性查找的特点(新手记)

  • 优点:简单,无需数组有序,代码易写;

  • 缺点:效率低,数组越长,遍历次数越多(最坏要遍历全部元素);

  • 适用场景:小容量数组、无序数组。

二、数组元素的排序:冒泡排序(让大元素 "浮" 到末尾)

排序是把数组元素按指定顺序(升序 / 降序)排列,冒泡排序 是新手必学的基础排序算法,核心是 "相邻元素比较交换,大的元素逐步往后移,像气泡上浮一样"。

2.1 生活化类比

把收纳盒里的成绩纸条按 "从低到高" 摆好:

  • 从第一个纸条开始,和旁边的纸条比,分数高的往右挪;

  • 第一轮比完,最高分的纸条会到最右边(像气泡浮到顶部);

  • 第二轮只比前 n-1 个纸条,确定次高分的位置;

  • 重复直到所有纸条排好。

2.2 核心原理(升序排序)

  1. 外层循环:控制排序轮数,共需数组长度-1轮(最后一个元素不用比);

  2. 内层循环:每轮遍历未排序的元素,相邻元素比较,若前 > 后则交换;

  3. 每轮结束:当前未排序部分的最大值 "浮" 到末尾。

2.3 基础代码实现(冒泡排序升序)

java 复制代码
public class ArrayBubbleSort {
    // 冒泡排序方法:传入int数组,按升序排序
    public static void bubbleSort(int[] arr) {
        // 外层循环:控制轮数,共arr.length-1轮
        for (int i = 0; i < arr.length - 1; i++) {
            // 内层循环:每轮比较到"未排序的最后一位",即arr.length-1-i
            for (int j = 0; j < arr.length - 1 - i; j++) {
                // 前一个元素 > 后一个,交换位置(升序)
                if (arr[j] > arr[j+1]) {
                    // 临时变量存值,完成交换
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] scores = {90, 85, 95, 88, 92};
        System.out.println("排序前:");
        for (int score : scores) {
            System.out.print(score + " "); // 输出:90 85 95 88 92
        }
        
        bubbleSort(scores); // 调用排序
        
        System.out.println("\n排序后(升序):");
        for (int score : scores) {
            System.out.print(score + " "); // 输出:85 88 90 92 95
        }
    }
}

2.4 冒泡排序优化(避免无效循环)

如果数组提前排好序,基础版还会继续循环,优化思路:加 "是否交换" 的标志位,没交换说明已排好,直接结束。

java 复制代码
public static void bubbleSortOptimize(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        boolean isSwapped = false; // 标志位:本轮是否交换过
        for (int j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] > arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                isSwapped = true; // 标记有交换
            }
        }
        if (!isSwapped) {
            break; // 本轮没交换,说明已排好,直接退出
        }
    }
}

三、数组元素的增删改:长度固定也能 "灵活操作"

数组长度一旦创建就不能改,但可以通过 "新建数组 + 拷贝元素" 实现增删,修改则直接通过索引赋值即可。

3.1 最简单:元素修改(直接替换)

修改是数组最基础的操作,直接通过 "数组名 [索引] = 新值" 替换,无需复杂逻辑。

java 复制代码
public class ArrayModify {
    public static void main(String[] args) {
        int[] scores = {90, 85, 95};
        // 修改索引1的元素(85分→92分)
        scores[1] = 92;
        System.out.println("修改后数组:");
        for (int score : scores) {
            System.out.print(score + " "); // 输出:90 92 95
        }
    }
}

3.2 数组新增元素(扩容思路)

核心逻辑:新建一个 "长度 + 1" 的数组,拷贝原数组所有元素,最后把新元素放到末尾。

java 复制代码
public class ArrayAdd {
    // 数组新增元素:arr原数组,newVal新增值
    public static int[] addElement(int[] arr, int newVal) {
        // 1. 新建长度+1的数组
        int[] newArr = new int[arr.length + 1];
        // 2. 拷贝原数组元素到新数组
        for (int i = 0; i < arr.length; i++) {
            newArr[i] = arr[i];
        }
        // 3. 新元素放到新数组末尾
        newArr[arr.length] = newVal;
        // 4. 返回新数组(原数组不变)
        return newArr;
    }

    public static void main(String[] args) {
        int[] scores = {90, 85, 95};
        int[] newScores = addElement(scores, 88); // 新增88分
        
        System.out.println("新增后数组:");
        for (int score : newScores) {
            System.out.print(score + " "); // 输出:90 85 95 88
        }
    }
}

3.3 数组删除元素(前移覆盖)

核心逻辑:找到要删除元素的索引,把该索引后面的元素逐个往前移,最后把末尾元素置为默认值(或新建长度 - 1 的数组)。

java 复制代码
public class ArrayDelete {
    // 数组删除元素:arr原数组,delIndex要删除的索引
    public static int[] deleteElement(int[] arr, int delIndex) {
        // 校验索引合法性
        if (delIndex < 0 || delIndex >= arr.length) {
            System.out.println("索引不合法,删除失败");
            return arr;
        }
        // 1. 新建长度-1的数组
        int[] newArr = new int[arr.length - 1];
        // 2. 拷贝删除索引前的元素
        for (int i = 0; i < delIndex; i++) {
            newArr[i] = arr[i];
        }
        // 3. 拷贝删除索引后的元素(往前移一位)
        for (int i = delIndex; i < newArr.length; i++) {
            newArr[i] = arr[i+1];
        }
        return newArr;
    }

    public static void main(String[] args) {
        int[] scores = {90, 85, 95, 88};
        int[] newScores = deleteElement(scores, 2); // 删除索引2的95分
        
        System.out.println("删除后数组:");
        for (int score : newScores) {
            System.out.print(score + " "); // 输出:90 85 88
        }
    }
}

3.4 关键提醒:数组增删的本质

数组长度固定,所谓 "增删" 其实是创建新数组 + 拷贝原元素,原数组不会改变,新手不要试图直接修改原数组的长度。

四、数组的拷贝:System.arraycopy ()(备份收纳盒)

数组拷贝是 "复制一份数组的全部 / 部分元素",Java 提供了System.arraycopy()方法,比手动 for 循环拷贝效率更高。

4.1 方法参数详解(新手记牢)

java 复制代码
System.arraycopy(源数组, 源起始索引, 目标数组, 目标起始索引, 拷贝长度);
  • 源数组:要拷贝的原数组;

  • 源起始索引:从原数组的哪个索引开始拷贝;

  • 目标数组:拷贝到的新数组;

  • 目标起始索引:新数组从哪个索引开始接收;

  • 拷贝长度:要拷贝的元素个数。

4.2 代码示例(拷贝全部 / 部分元素)

java 复制代码
public class ArrayCopy {
    public static void main(String[] args) {
        int[] scores = {90, 85, 95, 88, 92};
        
        // 示例1:拷贝全部元素(备份数组)
        int[] copyAll = new int[scores.length];
        System.arraycopy(scores, 0, copyAll, 0, scores.length);
        System.out.println("拷贝全部元素:");
        for (int score : copyAll) {
            System.out.print(score + " "); // 输出:90 85 95 88 92
        }

        // 示例2:拷贝部分元素(拷贝索引1到3的元素,共3个)
        int[] copyPart = new int[3];
        System.arraycopy(scores, 1, copyPart, 0, 3);
        System.out.println("\n拷贝部分元素:");
        for (int score : copyPart) {
            System.out.print(score + " "); // 输出:85 95 88
        }
    }
}

4.3 应用场景(新手常用)

  • 数组扩容 / 缩容:新增 / 删除元素时,拷贝原数组元素到新数组;

  • 数组备份:避免原数组修改影响数据;

  • 截取数组片段:只拷贝需要的部分元素。

4.4 避坑提醒:数组拷贝的边界校验

拷贝前要确保:

  • 拷贝长度 ≤ 源数组剩余元素个数(源起始索引 + 拷贝长度 ≤ 源数组长度);

  • 拷贝长度 ≤ 目标数组剩余空间(目标起始索引 + 拷贝长度 ≤ 目标数组长度);否则会抛出ArrayIndexOutOfBoundsException

五、新手必避的 5 个 "操作致命坑"

5.1 坑 1:冒泡排序内层循环条件写错

  • 错误示例:

    java 复制代码
    // 内层循环没减i,重复比较已排好的元素
    for (int j = 0; j < arr.length - 1; j++) {
    }
  • 避坑:固定写j < arr.length - 1 - i,每轮少比较已排好的 i 个元素。

5.2 坑 2:新增 / 删除元素时直接修改原数组

  • 错误示例:

    java 复制代码
    // 以为能直接给原数组新增元素,实际编译报错
    scores.length = scores.length + 1;
  • 避坑:增删必须创建新数组,原数组长度无法修改。

5.3 坑 3:线性查找返回布尔值,不返回索引

  • 错误示例:

    java 复制代码
    // 只能判断有没有,没法知道位置,实用性低
    public static boolean search(int[] arr, int target) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == target) return true;
        }
        return false;
    }
  • 避坑:优先返回索引(找到返回索引,没找到返回 - 1),更灵活。

5.4 坑 4:数组拷贝时目标数组未初始化

  • 错误示例:

    java 复制代码
    int[] copyAll = null;
    System.arraycopy(scores, 0, copyAll, 0, scores.length); // 空指针异常
  • 避坑:拷贝前必须初始化目标数组,且长度足够。

5.5 坑 5:删除元素时忽略索引合法性校验

  • 错误示例:

    java 复制代码
    // 传入负数索引或超出长度的索引,运行时报错
    deleteElement(scores, -1);
  • 避坑:删除前先校验delIndex >= 0 && delIndex < arr.length

总结

这一节咱们掌握了数组的 4 个核心常见操作,记住 3 个核心点:

  1. 查找:线性查找适合无序数组,找到返回索引、没找到返回 - 1;

  2. 排序:冒泡排序核心是 "相邻比较交换,每轮确定一个最大值",加标志位可优化;

  3. 增删改拷贝:修改直接赋值,增删需新建数组,拷贝优先用 System.arraycopy ()。

这些操作是数组实操的基础,后续学习二维数组、集合等内容都会用到,掌握好这些,处理批量数据就会更灵活。

相关推荐
编程火箭车1 天前
【Java SE 基础学习打卡】35 数组元素的访问与遍历
数组索引·java se·java 基础·java 数组·数组元素访问与遍历·普通 for 循环·增强 for 循环
编程火箭车4 天前
【Java SE 基础学习打卡】34 数组的定义与初始化
java se·java 基础·java 数组·数组定义与初始化·静态初始化·动态初始化·length 属性
编程火箭车5 天前
【Java SE 基础学习打卡】33 数组的概述
java se·java 基础·数组概述·数组核心特征·java 数组·批量存储数据·连续内存存储
haoly19896 天前
数据结构与算法篇-线性查找-存在性问题
线性查找·递归分析
编程火箭车6 天前
【Java SE 基础学习打卡】32 方法的嵌套调用与递归调用
java se·java 基础·java 方法·方法嵌套调用·方法递归调用·递归终止条件·递归应用场景
haoly19897 天前
数据结构与算法篇-线性查找-获取目标元素位置信息
线性查找·递归分析思路
编程火箭车7 天前
【Java SE 基础学习打卡】31 方法的返回值与void关键字
java se·java 基础·return 语句·编程小白入门·java 方法·方法返回值·void 关键字
编程火箭车10 天前
【Java SE 基础学习打卡】28 方法的定义与调用
java se·参数传递·返回值·java 基础·新手避坑·java 方法·方法定义与调用