目录
- 前言
- 一、数组元素的查找:线性查找(翻收纳盒找东西)
-
- [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轮(最后一个元素不用比); -
内层循环:每轮遍历未排序的元素,相邻元素比较,若前 > 后则交换;
-
每轮结束:当前未排序部分的最大值 "浮" 到末尾。
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:数组拷贝时目标数组未初始化
-
错误示例:
javaint[] 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;
-
排序:冒泡排序核心是 "相邻比较交换,每轮确定一个最大值",加标志位可优化;
-
增删改拷贝:修改直接赋值,增删需新建数组,拷贝优先用 System.arraycopy ()。
这些操作是数组实操的基础,后续学习二维数组、集合等内容都会用到,掌握好这些,处理批量数据就会更灵活。