目录
- 前言
- 一、数组元素的查找:线性查找(翻收纳盒找东西)
- 1.1 生活化类比
- 1.2 核心原理
- 1.3 代码示例(找指定成绩,返回索引)
- 1.4 线性查找的特点(新手记)
- 二、数组元素的排序:冒泡排序(让大元素 “浮” 到末尾)
- 2.1 生活化类比
- 2.2 核心原理(升序排序)
- 2.3 基础代码实现(冒泡排序升序)
- 2.4 冒泡排序优化(避免无效循环)
- 三、数组元素的增删改:长度固定也能 “灵活操作”
- 3.1 最简单:元素修改(直接替换)
- 3.2 数组新增元素(扩容思路)
- 3.3 数组删除元素(前移覆盖)
- 3.4 关键提醒:数组增删的本质
- 四、数组的拷贝:System.arraycopy ()(备份收纳盒)
- 4.1 方法参数详解(新手记牢)
- 4.2 代码示例(拷贝全部 / 部分元素)
- 4.3 应用场景(新手常用)
- 4.4 避坑提醒:数组拷贝的边界校验
- 五、新手必避的 5 个 “操作致命坑”
- 5.1 坑 1:冒泡排序内层循环条件写错
- 5.2 坑 2:新增 / 删除元素时直接修改原数组
- 5.3 坑 3:线性查找返回布尔值,不返回索引
- 5.4 坑 4:数组拷贝时目标数组未初始化
- 5.5 坑 5:删除元素时忽略索引合法性校验
- 总结
前言
上一节咱们掌握了数组元素的访问和遍历,现在要落地到实际编程场景:比如从成绩数组里找有没有 95 分的学生(查找)、把成绩按从低到高排好序(排序)、给数组新增一个学生成绩(新增)、删掉无效的成绩(删除)、备份一份成绩数组(拷贝)。
新手容易觉得 “数组长度固定” 就没法增删元素,也容易被冒泡排序的 “相邻交换” 绕晕,这一节咱们就用 “整理收纳盒里的东西” 作为类比,把数组的查找、排序、增删改、拷贝这 4 个核心操作讲透,每个操作都给可运行的代码,还会讲优化思路和避坑点,让新手能直接落地使用!
一、数组元素的查找:线性查找(翻收纳盒找东西)
数组查找是 “从一堆元素里找指定值”,最基础的是线性查找(也叫顺序查找),适合无序数组,原理简单易懂。
1.1 生活化类比
想从装满成绩纸条的收纳盒里找 “95 分” 的纸条:
从第一个纸条开始,逐个打开看;
找到就停下,没找到就翻到最后一个;
核心:按顺序遍历,逐个匹配,适合杂乱的收纳盒(无序数组)。
1.2 核心原理
遍历数组的每个元素,和目标值逐一比较:
匹配成功:返回该元素的索引;
遍历结束仍未匹配:返回 - 1(约定俗成的 “未找到” 标识)。
1.3 代码示例(找指定成绩,返回索引)
publicclassArraySearch{// 线性查找方法:arr是数组,target是要找的目标值publicstaticintlinearSearch(int[]arr,inttarget){// 遍历数组,逐个匹配for(inti=0;i<arr.length;i++){if(arr[i]==target){returni;// 找到,返回索引}}return-1;// 没找到,返回-1}publicstaticvoidmain(String[]args){int[]scores={90,85,95,88,92};inttarget=95;intindex=linearSearch(scores,target);if(index!=-1){System.out.println("找到"+target+"分,索引是:"+index);}else{System.out.println("没找到"+target+"分");}}}执行结果:
找到95分,索引是:21.4 线性查找的特点(新手记)
优点:简单,无需数组有序,代码易写;
缺点:效率低,数组越长,遍历次数越多(最坏要遍历全部元素);
适用场景:小容量数组、无序数组。
二、数组元素的排序:冒泡排序(让大元素 “浮” 到末尾)
排序是把数组元素按指定顺序(升序 / 降序)排列,冒泡排序是新手必学的基础排序算法,核心是 “相邻元素比较交换,大的元素逐步往后移,像气泡上浮一样”。
2.1 生活化类比
把收纳盒里的成绩纸条按 “从低到高” 摆好:
从第一个纸条开始,和旁边的纸条比,分数高的往右挪;
第一轮比完,最高分的纸条会到最右边(像气泡浮到顶部);
第二轮只比前 n-1 个纸条,确定次高分的位置;
重复直到所有纸条排好。
2.2 核心原理(升序排序)
外层循环:控制排序轮数,共需
数组长度-1轮(最后一个元素不用比);内层循环:每轮遍历未排序的元素,相邻元素比较,若前 > 后则交换;
每轮结束:当前未排序部分的最大值 “浮” 到末尾。
2.3 基础代码实现(冒泡排序升序)
publicclassArrayBubbleSort{// 冒泡排序方法:传入int数组,按升序排序publicstaticvoidbubbleSort(int[]arr){// 外层循环:控制轮数,共arr.length-1轮for(inti=0;i<arr.length-1;i++){// 内层循环:每轮比较到“未排序的最后一位”,即arr.length-1-ifor(intj=0;j<arr.length-1-i;j++){// 前一个元素 > 后一个,交换位置(升序)if(arr[j]>arr[j+1]){// 临时变量存值,完成交换inttemp=arr[j];arr[j]=arr[j+1];arr[j+1]=temp;}}}}publicstaticvoidmain(String[]args){int[]scores={90,85,95,88,92};System.out.println("排序前:");for(intscore:scores){System.out.print(score+" ");// 输出:90 85 95 88 92}bubbleSort(scores);// 调用排序System.out.println("\n排序后(升序):");for(intscore:scores){System.out.print(score+" ");// 输出:85 88 90 92 95}}}2.4 冒泡排序优化(避免无效循环)
如果数组提前排好序,基础版还会继续循环,优化思路:加 “是否交换” 的标志位,没交换说明已排好,直接结束。
publicstaticvoidbubbleSortOptimize(int[]arr){for(inti=0;i<arr.length-1;i++){booleanisSwapped=false;// 标志位:本轮是否交换过for(intj=0;j<arr.length-1-i;j++){if(arr[j]>arr[j+1]){inttemp=arr[j];arr[j]=arr[j+1];arr[j+1]=temp;isSwapped=true;// 标记有交换}}if(!isSwapped){break;// 本轮没交换,说明已排好,直接退出}}}三、数组元素的增删改:长度固定也能 “灵活操作”
数组长度一旦创建就不能改,但可以通过 “新建数组 + 拷贝元素” 实现增删,修改则直接通过索引赋值即可。
3.1 最简单:元素修改(直接替换)
修改是数组最基础的操作,直接通过 “数组名 [索引] = 新值” 替换,无需复杂逻辑。
publicclassArrayModify{publicstaticvoidmain(String[]args){int[]scores={90,85,95};// 修改索引1的元素(85分→92分)scores[1]=92;System.out.println("修改后数组:");for(intscore:scores){System.out.print(score+" ");// 输出:90 92 95}}}3.2 数组新增元素(扩容思路)
核心逻辑:新建一个 “长度 + 1” 的数组,拷贝原数组所有元素,最后把新元素放到末尾。
publicclassArrayAdd{// 数组新增元素:arr原数组,newVal新增值publicstaticint[]addElement(int[]arr,intnewVal){// 1. 新建长度+1的数组int[]newArr=newint[arr.length+1];// 2. 拷贝原数组元素到新数组for(inti=0;i<arr.length;i++){newArr[i]=arr[i];}// 3. 新元素放到新数组末尾newArr[arr.length]=newVal;// 4. 返回新数组(原数组不变)returnnewArr;}publicstaticvoidmain(String[]args){int[]scores={90,85,95};int[]newScores=addElement(scores,88);// 新增88分System.out.println("新增后数组:");for(intscore:newScores){System.out.print(score+" ");// 输出:90 85 95 88}}}3.3 数组删除元素(前移覆盖)
核心逻辑:找到要删除元素的索引,把该索引后面的元素逐个往前移,最后把末尾元素置为默认值(或新建长度 - 1 的数组)。
publicclassArrayDelete{// 数组删除元素:arr原数组,delIndex要删除的索引publicstaticint[]deleteElement(int[]arr,intdelIndex){// 校验索引合法性if(delIndex<0||delIndex>=arr.length){System.out.println("索引不合法,删除失败");returnarr;}// 1. 新建长度-1的数组int[]newArr=newint[arr.length-1];// 2. 拷贝删除索引前的元素for(inti=0;i<delIndex;i++){newArr[i]=arr[i];}// 3. 拷贝删除索引后的元素(往前移一位)for(inti=delIndex;i<newArr.length;i++){newArr[i]=arr[i+1];}returnnewArr;}publicstaticvoidmain(String[]args){int[]scores={90,85,95,88};int[]newScores=deleteElement(scores,2);// 删除索引2的95分System.out.println("删除后数组:");for(intscore:newScores){System.out.print(score+" ");// 输出:90 85 88}}}3.4 关键提醒:数组增删的本质
数组长度固定,所谓 “增删” 其实是创建新数组 + 拷贝原元素,原数组不会改变,新手不要试图直接修改原数组的长度。
四、数组的拷贝:System.arraycopy ()(备份收纳盒)
数组拷贝是 “复制一份数组的全部 / 部分元素”,Java 提供了System.arraycopy()方法,比手动 for 循环拷贝效率更高。
4.1 方法参数详解(新手记牢)
System.arraycopy(源数组,源起始索引,目标数组,目标起始索引,拷贝长度);源数组:要拷贝的原数组;
源起始索引:从原数组的哪个索引开始拷贝;
目标数组:拷贝到的新数组;
目标起始索引:新数组从哪个索引开始接收;
拷贝长度:要拷贝的元素个数。
4.2 代码示例(拷贝全部 / 部分元素)
publicclassArrayCopy{publicstaticvoidmain(String[]args){int[]scores={90,85,95,88,92};// 示例1:拷贝全部元素(备份数组)int[]copyAll=newint[scores.length];System.arraycopy(scores,0,copyAll,0,scores.length);System.out.println("拷贝全部元素:");for(intscore:copyAll){System.out.print(score+" ");// 输出:90 85 95 88 92}// 示例2:拷贝部分元素(拷贝索引1到3的元素,共3个)int[]copyPart=newint[3];System.arraycopy(scores,1,copyPart,0,3);System.out.println("\n拷贝部分元素:");for(intscore:copyPart){System.out.print(score+" ");// 输出:85 95 88}}}4.3 应用场景(新手常用)
数组扩容 / 缩容:新增 / 删除元素时,拷贝原数组元素到新数组;
数组备份:避免原数组修改影响数据;
截取数组片段:只拷贝需要的部分元素。
4.4 避坑提醒:数组拷贝的边界校验
拷贝前要确保:
拷贝长度 ≤ 源数组剩余元素个数(源起始索引 + 拷贝长度 ≤ 源数组长度);
拷贝长度 ≤ 目标数组剩余空间(目标起始索引 + 拷贝长度 ≤ 目标数组长度);否则会抛出
ArrayIndexOutOfBoundsException。
五、新手必避的 5 个 “操作致命坑”
5.1 坑 1:冒泡排序内层循环条件写错
错误示例:
// 内层循环没减i,重复比较已排好的元素for(intj=0;j<arr.length-1;j++){}避坑:固定写
j < arr.length - 1 - i,每轮少比较已排好的 i 个元素。
5.2 坑 2:新增 / 删除元素时直接修改原数组
错误示例:
// 以为能直接给原数组新增元素,实际编译报错scores.length=scores.length+1;避坑:增删必须创建新数组,原数组长度无法修改。
5.3 坑 3:线性查找返回布尔值,不返回索引
错误示例:
// 只能判断有没有,没法知道位置,实用性低publicstaticbooleansearch(int[]arr,inttarget){for(inti=0;i<arr.length;i++){if(arr[i]==target)returntrue;}returnfalse;}避坑:优先返回索引(找到返回索引,没找到返回 - 1),更灵活。
5.4 坑 4:数组拷贝时目标数组未初始化
错误示例:
int[]copyAll=null;System.arraycopy(scores,0,copyAll,0,scores.length);// 空指针异常避坑:拷贝前必须初始化目标数组,且长度足够。
5.5 坑 5:删除元素时忽略索引合法性校验
错误示例:
// 传入负数索引或超出长度的索引,运行时报错deleteElement(scores,-1);避坑:删除前先校验
delIndex >= 0 && delIndex < arr.length。
总结
这一节咱们掌握了数组的 4 个核心常见操作,记住 3 个核心点:
查找:线性查找适合无序数组,找到返回索引、没找到返回 - 1;
排序:冒泡排序核心是 “相邻比较交换,每轮确定一个最大值”,加标志位可优化;
增删改拷贝:修改直接赋值,增删需新建数组,拷贝优先用 System.arraycopy ()。
这些操作是数组实操的基础,后续学习二维数组、集合等内容都会用到,掌握好这些,处理批量数据就会更灵活。