@[toc]
一、数组知识回顾
之前咱们学了数组的基础知识,并做了小练习。
还不清楚数组基本知识的戳这里,非常详尽!!!
本次将继续带大家做数组练习,数组非常重要,希望大家能够熟练掌握。
接下来先梳理一下前面说过的知识点,看看你是否都知道呢,不清楚的可以看我之前的文章哦。
(1)概念
数组是用来存储一组相同数据类型的数据的容器。
将一组数据统一地管理起来。
(2)特点
1.数组是一个引用数据类型。
2.数组内部可以存储的元素,可以是基本类型,也可以是引用类型。
3.数组是在堆内存中的一串连续的地址。
4.数组在初始化时必须指定长度及内部存储元素类型(静态/动态都有)。
5.如果需要用变量来进行存储,变量空间在栈内存中,变量中存储的是数组引用(地址)。
6.堆内存的数组空间长度一旦确定,不能再次发生改变。
(3)创建数组
数组的定义(创建 声明):
数组内部类型[] 数组名字;
AI 代码解读
如:
int[] array;
AI 代码解读
(4)数组的初始化
数组的初始化(创建并赋值):
①静态初始化-->有长度(元素个数),有元素(元素能够看得见)
数组内部类型[] 数组名字=new 数组内部类型[]{元素};
AI 代码解读
如:
int[] array=new int[]{1,2,3,4,5};
//这个数组(array),有5个元素,具体元素也能看得见。
AI 代码解读
②动态初始化-->只有长度(元素个数),没有元素(不是真的没有,都是默认值)
数组内部类型[] 数组名字=new 数组内部类型[元素个数];
//元素个数只能是0以上的(包括0)。
//如果是0,就是创建了一个长度为0的数组,没有意义。
//如果长度小于0,会出现一个“编译时异常”,数组长度不合法(Negative Array Size Exception)的错误。
AI 代码解读
如:
int[] array=new int[5];
//这个数组(array),有5个元素,具体元素看不见,都是默认值,int类型的默认值为0
AI 代码解读
这个初始化的过程,就是在堆内存中实实在在地创建了一个数组。
就是在堆内存中开辟的一串连续的地址空间 ,并赋了值。
:warning: 注意:
栈内存中没有元素,就是真的没有。
栈内存中都是变量,变量创建出来,,不往里面存东西,就是真的没有元素。
==栈内存里的变量没有初始化,就是真的没有元素。==
堆内存的元素,创建出来都是有默认值的。
现在是数组,创建在堆内存里,以后的对象也在堆内存里。
堆内存里的对象空间,里面的每一个值,如果不给它们赋值,都是有默认值存在的。
==堆内存里都是有默认值的。==
(5)数组元素的访问
数组元素的访问(存/取),是通过元素在数组中的位置(index索引)来访问的。
数组的索引从0
开始,一直到数组长度-1
结束。
如果索引出现范围以外的值(比0小或比长度减一大),就会产生一个运行时异常,
即:Array Index Out Of Bounds Exception
(数组索引越界)。
要求:1.记住错误英文名 2.知道这个错误如何产生的 3.以后遇到了会更正错误。
(6)数组元素的遍历
数组的遍历(轮询):
正常的for循环
/ 增强for循环
。
正常for循环: 有索引,可以赋值,可以取值。但写法相对比较麻烦。
增强for循环:写法相对比较简单。但是没有索引,找不到位置,只能取值(用变量来接收值)。存的值在变量里,并不在数组里。
(7)内存结构
数组本身是一个引用数据类型。
注意基本数据类型和引用数据类型之间的区别。
它们在内存结构上的区别,即:基本类型存的是值,引用类型存的是址。
注:针对这个知识点,我专门写了一篇博文,不清楚的戳这里。
二、数组练习之互换元素
案例一
01 需求
:car:给定一个数组a{1,2,3,4,5,6},将这个数组中的元素头尾对应互换位置。
02 分析
:cake:下面来剖析一下这个问题。
(1)创建数组
根据题意,创建一个数组,取名为array
。
int[] array;
AI 代码解读
(2)初始化
明显,数组内元素比较少,用静态初始化。
标准写法:
int[] array=new int[]{1,2,3,4,5,6};
AI 代码解读
简洁写法:
int[] array={1,2,3,4,5,6};
AI 代码解读
(3)循环解决
我们要将这个数组里边的元素头尾互换,是重复同一件事情。
既然是同一件事情,就可以用循环来解决。
先来找一下规律。
int[] array={1,2,3,4,5,6};
array[0]<--->array[5]
array[1]<--->array[4]
array[2]<--->array[3]
array[i]<--->array[5-i]
AI 代码解读
循环一共执行3次,从几开始都可以,之前说过,从1-3可以,从10-12也可以。
但是,如果执行3次,可以和索引号对上,就比较方便。
==索引号也是个变量,如果循环变量和索引号变量变化一致,就会省去一个变量。==
索引号从0开始,那我们可以让循环变量也从0开始。
到什么时候结束?一共要循环3次。
那么循环三要素就有了。
for(int i=0;i<3;i++){ //控制3次
}
AI 代码解读
接下来是循环体,通过上面的规律,很容易得出这两个元素互换:
array[i]<--->array[5-i]
AI 代码解读
既然要互换元素,就要找一个中间变量,类型要和数组内元素数据类型一致,为int
类型。
则咱们创建一个中间变量int x
,并互换头尾元素:
int x=array[i];
array[i]=array[5-i];
array[5-i]=x;
AI 代码解读
整体for
循环就写好了:
for(int i=0;i<3;i++){ //控制3次
int x=array[i];
array[i]=array[5-i];
array[5-i]=x;
}
AI 代码解读
(4)输出
接下来,咱们输出结果看一看。
这里用增强for
循环输出一下,因为这个for循环比较简单。
不知道的戳这里,在数组元素遍历第三点说过,具体位置:
写出来的代码如下:
for(int v:array){
System.out.println(v); //用变量v来接收数组内的元素,冒号后边是要遍历的数组
}
AI 代码解读
03 代码及执行
(1)代码
加上头尾,整体代码如下:
public class Test{
public static void main(String[] args){
//1.创建数组并初始化
int[] array={1,2,3,4,5,6};
//2.循环控制
for(int i=0;i<3;i++){ //控制3次
int x=array[i];
array[i]=array[5-i];
array[5-i]=x;
}
//3.输出
for(int v:array){
System.out.println(v);
}
}
}
AI 代码解读
(2)执行结果
执行结果如下:
可以看见,数组元素头尾互换了。
04 延拓
(1)问题
上面的题比较简单,但这里我想强调一点。
这里的数组是给我们的,元素啥的都是很清楚的。
如果给了我们一个不确定的数组,数组内部的元素我们不知道(这个数组是别人经过一系列操作生成的,不知道有什么元素)。
此时,需要我们将这个数组内部元素头尾互换。
那该咋办呢?
(2)分析
此时我们手里面是有这个数组的名字的,只需要在循环的时候,==将循环次数修改一下就好==。
看一下规律,6个数循环3次,7个数也是换3次;8个数换4次,9个数也换4次。那么就是换数组长度/2
次。
因为是/
号,所以小数点后的值抹掉。
那么,循环三要素就可以这样写:
for(int i=0;i<array.length/2;i++){ //循环次数-->数组长度/2 -->array.length/2
}
AI 代码解读
然后是循环体部分,看一下规律:
int[] array={1,2,3,4,5,6};
array[0]<--->array[5]
array[1]<--->array[4]
array[2]<--->array[3]
得到:
array[i]<--->array[5-i] ---> array[(数组长度-1)-i] --> array[(array.length-1)-i]
AI 代码解读
然后交换元素头尾:
int x=array[i];
array[i]=array[(array.length-1)-i];
array[(array.length-1)-i]=x;
AI 代码解读
那么循环体加上三要素,循环部分就是这样:
for(int i=0;i<array.length/2;i++){
int x=array[i];
array[i]=array[(array.length-1)-i];
array[(array.length-1)-i]=x;
}
AI 代码解读
这样的话,数组长度变化,结果就会跟着变化。
可以==增强代码的复用性==。
(3)优化代码及执行结果
好啦,最后优化的代码:
public class Test{
public static void main(String[] args){
//1.创建数组并初始化
int[] array={1,2,3,4,5,6};
//2.循环控制
for(int i=0;i<array.length/2;i++){ //控制交换次数,数组长度的一半
int x=array[i];
array[i]=array[(array.length-1)-i]; //首元素与最后元素互换
array[(array.length-1)-i]=x;
}
//3.输出
for(int v:array){
System.out.println(v);
}
}
}
AI 代码解读
执行结果如下:
(4)改数组长度
同样,大家可以将数组改一改,再试一试,看看结果。
比如将数组改为四个元素1、2、3、4,然后执行。
public class Test{
public static void main(String[] args){
//1.创建数组并初始化
int[] array={1,2,3,4};
//2.循环控制
for(int i=0;i<array.length/2;i++){ //控制交换次数,数组长度的一半
int x=array[i];
array[i]=array[(array.length-1)-i]; //首元素与最后元素互换
array[(array.length-1)-i]=x;
}
//3.输出
for(int v:array){
System.out.println(v);
}
}
}
AI 代码解读
最后结果,就是头尾互换:
案例二
01 需求
:car:给定两个数组a{1,2,3,4},b{5,6,7,8},将两个数组内的元素对应位置互换。
02 分析
:cake:下面来剖析一下这个问题。
之前在变量互换的时候,说过三种方式。第一种找中间量互换,第二种利用加和的方式互换,第三种利用异或符号互换。
现在是数组,里面有好多元素,那该如何互换呢?
(1)创建两个数组
根据题意,我们需要先创建两个数组a
和b
。
int[] a;
int[] b;
AI 代码解读
(2)初始化
很明显,题目都已经告诉数组元素了,而且元素个数不是很多,自然用静态初始化啦。
标准写法:
int[] a=new int[]{1,2,3,4};
int[] b=new int[]{5,6,7,8};
AI 代码解读
简洁写法:
int[] a={1,2,3,4};
int[] b={5,6,7,8};
AI 代码解读
(3)利用循环控制
元素对应位置的互换。
每个数组,都有四个元素,每一次交换两个数字,一共换四次。
第一次,1和5交换;第二次,2和6互换……
换4次,每次都是做一样的事情。
既然是重复做同一个事情,自然就想到了利用循环解决。
①循环三要素
循环一共执行4次,从几开始都可以,之前说过,从1-4可以,从10-13也可以。
但是,如果执行4次,可以和索引号对上,就比较方便。
==索引号也是个变量,如果循环变量和索引号变量变化一致,就会省去一个变量。==
索引号从0开始,那我们可以让循环变量也从0开始。
到什么时候结束?
一共要循环4次,4是元素个数(数组长度),可以用a.length
表示。(此时a数组与b数组长度一致)
那么循环三要素就有了。
for(int i=0;i<a.length;i++){
}
AI 代码解读
这里的变量i
不仅是循环变量,还可以当索引号使用,因为两者变化一致。
②循环体
循环体里面就是写两个数组的对应位置互换元素。
找一下规律:
a[0]<--->b[0]
a[1]<--->b[1]
a[2]<--->b[2]
a[3]<--->b[3]
得出:
a[i]<--->b[i]
AI 代码解读
规律找出来了,接下来是互换元素。
此时我们发现,a[i]
和b[i]
可以当作两个变量,两个变量互换位置,就是之前学的啦。
这里用中间变量来做示范。
int x=a[i]; //先将a[i]的值存入中间变量x中
a[i]=b[i]; //再将b[i]的值存入a[i]中
b[i]=x; //最后将x的值存入b[i]中
AI 代码解读
这里放个大致的图解释一下(基础不好的对照理解哈):
③整体循环写完了,看一下循环整体代码:
for(int i=0;i<a.length;i++){
int x=a[i];
a[i]=b[i];
b[i]=x;
}
AI 代码解读
(4)输出
分别输出看一下。
这里用增强for
循环输出一下,因为这个for循环比较简单。
不知道的戳这里,在数组元素遍历第三点说过,具体位置:
输出a数组
:
for(int v:a){ //用变量v来接收数组内的元素,冒号后边是要遍历的数组
System.out.println(v);
}
AI 代码解读
输出b数组
:
for(int v:b){ //用变量v来接收数组内的元素,冒号后边是要遍历的数组
System.out.println(v); //变量v可以和上边的v重名,因为是两个不同的循环,互不干扰
}
AI 代码解读
03 代码及执行
(1)代码
加上头尾,整体代码如下:
public class Test{
public static void main(String[] args){
//1.创建两个数组
int[] a={1,2,3,4};
int[] b={5,6,7,8};
//2.元素对应位置互换
for(int i=0;i<a.length;i++){
int x=a[i];
a[i]=b[i];
b[i]=x;
}
//3.输出
System.out.println("a数组:");
for(int v:a){
System.out.println(v);
}
System.out.println("b数组:");
for(int v:b){
System.out.println(v);
}
}
}
AI 代码解读
(2)执行结果
输出看一下结果哈。
04 补充
有的小伙伴可能想过用这个方式输出最后交换的数组:
System.out.println(a); System.out.println(b);
AI 代码解读
那这个方式对不对呢?
咱们来输出看一下。
整体代码:
public class Test{
public static void main(String[] args){
//1.创建两个数组
int[] a={1,2,3,4};
int[] b={5,6,7,8};
//2.元素对应位置互换
for(int i=0;i<a.length;i++){
int x=a[i];
a[i]=b[i];
b[i]=x;
}
//3.输出
System.out.println(a);
System.out.println(b);
}
}
AI 代码解读
输出结果:
可以看到,编译时好使的,运行出来的东西是啥?
其实这不是乱码。
这是HashCode
码,这里输出的是int
类型的整数。(十六进制)
再来想一下数组的内存结构,如int[] a=new int[]{1,2,3,4};
。
a
是在栈内存中的一个内存空间,1,2,3,4
存储在堆内存中一个连续的空间里。
a
里面存的是一个地址,所以存的地址被经过HashCode计算,得了一串这样的东西。
==@
前面表示它的类型,[
就是数组类型,I
表示数组里面存的是int整数类型;后面是一串经过计算的数字(十六进制)。==
本题,我们是想输出a
数组对应位置里边的元素,而不是它的地址。
所以上面的输出方式不可取。
05 延拓
(1)分析拓展
这个题比较简单,但为什么说它呢?
我们会发现,这个题目中,两个数组的长度一致。
这就大大降低了题目难度。
如果是这样的两个数组呢?
int[] a={1,2,3,4};
int[] b={5,6,7,8,9,0};
AI 代码解读
我们想最终换成这样的结果:
int[] a={5,6,7,8,9,0};
int[] b={1,2,3,4};
AI 代码解读
两个数组长度不一致,就不能跟上面的代码一样了。
若还是上面的那种方式,来想一想。
互换部分的循环若是这样:
//2.元素对应位置互换
for(int i=0;i<a.length;i++){ //循环4次
int x=a[i];
a[i]=b[i];
b[i]=x;
}
AI 代码解读
那么就是循环4次,最终a
数组的元素就是这样:5,6,7,8。
b
数组的元素就是这样:1,2,3,4,9,0。
如果循环部分是这样:
//2.元素对应位置互换
for(int i=0;i<b.length;i++){ //循环6次
int x=a[i];
a[i]=b[i];
b[i]=x;
}
AI 代码解读
这样是循环6次,最终a
数组的元素就是这样:5,6,7,8。
b
数组的元素就是这样:1,2,3,4。
a
的长度还不够6个呢,数组越界啦。
(2)举例
那怎么办呢?
①先来分析一下之前两个数组长度相同(元素个数相同)的时候。
来举个小例子吧。
老师甲带班级a,有1号、2号、3号、4号同学;老师乙带班级b,有5号、6号、7号、8号同学。
现在想让老师甲教5号、6号、7号、8号同学,让老师乙教1号、2号、3号、4号同学。
按照刚才的分析,我们是让同学们互换座位。
即:1号同学去5号哦同学座位,5号同学去1号同学座位;
2号同学去6号同学座位,6号同学去2号同学座位;
…… ……
具体换座位的细节如下:
②再来分析一下两个数组长度不相同(元素个数不相同)的时候。
还是拿刚才的例子。
此时,a班级只有四个座位,分别是1、2、3、4;而b班级有6个座位,分别是5、6、7、8、9、0。
老师甲带a班级,老师乙带b班级。
同样,现在想让老师甲教5号、6号、7号、8号、9号、0号同学,让老师乙教1号、2号、3号、4号同学。
按照互换座位的方式是行不通的,因为a班级没有6个座位。
那怎么办呢?
交换老师呗!
让老师甲去b班级,让老师乙去a班级。
(3)分析
:question: 怎么换老师呢?
前提咱们已经了解了数组在内存中的结构啦。
简单回顾一下,然后再来分析。
①还是拿第一个长度相同的两个数组为例。
两个数组:
int[] a={1,2,3,4};
int[] b={5,6,7,8};
AI 代码解读
循环交换过程:
//2.元素对应位置互换
for(int i=0;i<a.length;i++){ //循环4次
int x=a[i];
a[i]=b[i];
b[i]=x;
}
AI 代码解读
来看一下循环交换内存图:
②第二个长度不相同的两个数组。
两个数组:
int[] a={1,2,3,4};
int[] b={5,6,7,8,9,0};
AI 代码解读
如果还是按照刚才的循环来存储,上面的就存不下下边六个元素啦。
数组的存储方式:右边是真实存在的元素,左边是两个地址。
既然刚才试了右边换元素不好使,==那就换左边呗!==
也就是刚才举例的,换老师。
让a
里面存五角星,那么a
就找的是下边的元素;让b
里面存三角形,那么b
就找的是上边的元素。
也就是老师甲去教b教室学生,老师乙去教a教室学生。不用学生自己挪动座位了。
如下图:
(4)解决
按照刚才的分析,循环办法并不是最好的解决方案。
因为它(==交换数组中对应元素==)不仅要循环好多次,还要受长度的限制。
那么接下来的方法(==直接交换变量a和b中的数组引用-->地址==),一次就搞定,不受长度的限制啦。
那么,咱们用交换地址的方式来解决一下。
我们交换两者(星星和三角形),就需要第三方中间变量的加入。以第三方为媒介,互换两者。
三角形和五角星是什么类型的?是int[]
数组类型的!
那么我们要创建的中间变量,自然也应该是int[]
数组类型的啦!
:soccer: 现在咱们创建int[] temp
为中间变量,交换三角形和五角星。
这个交换过程就很容易啦。
int[] temp=a;
a=b;
b=temp;
AI 代码解读
没有循环,一次搞定。
a
和b
本身是数组类型,里边存的是地址。那么就需要拿一个和它类型匹配的空间来接收a
并充当中间变量。
(5)整体代码及执行结果
①第一个问题(两个数组长度相同)的代码如下:
public class Test{
public static void main(String[] args){
//1.创建两个数组
int[] a={1,2,3,4};
int[] b={5,6,7,8};
//2.交换变量a和b中的数组引用(地址)
int[] temp=a;
a=b;
b=temp;
//3.输出
System.out.println("a数组:");
for(int v:a){
System.out.println(v);
}
System.out.println("b数组:");
for(int v:b){
System.out.println(v);
}
}
}
AI 代码解读
然后咱们执行看一下结果:
和刚才结果一致。
②第二个问题(数组长度不一致)的代码如下:
public class Test{
public static void main(String[] args){
//1.创建两个数组
int[] a={1,2,3,4};
int[] b={5,6,7,8,9,0};
//2.交换变量a和b中的数组引用(地址)
int[] temp=a;
a=b;
b=temp;
//3.输出
System.out.println("a数组:");
for(int v:a){
System.out.println(v);
}
System.out.println("b数组:");
for(int v:b){
System.out.println(v);
}
}
}
AI 代码解读
然后咱们执行看一下结果:
看一下代码,我们并未改动其他地方,仅仅==只是改了数组元素==。
由此可见,这个交换数组引用的方法更加完美!
有的小伙伴可能可以写出这个代码,但是不理解我上边说的底层原理,还是不行的哦。
能写出代码,并理解底层原理,才是最重要的!
三、叨叨
这次的小练习,还是很简单,但我想告诉大家,要沉下心,不能盲目刷题。
每一个题目,也许你会很快解决,但是,是不是最优解呢?是不是底层原理都知道呢?
相遇即是缘,希望我的文章对你有帮助~
如果有什么不对的地方,欢迎指正~