我在输入知识,也在用我理解后的方式进行输出
以下内容是 兔C 献给大家的 2022年1月1日 的 新年礼物
文章目录
1. 数组的概念
我们先用理解的形式说明一下什么是数组:
1.1关于兔C残篇
我们之前的残篇中提到过一个概念,就是java中的数据类型,分为两种,基本数据类型和引用数据类型,我们在兔C残篇方法的讲述中提到过,方法的返回值可以用基本数据类型,但是没有跟大家提到过引用数据类型,是因为我们兔C残篇还没有进展到面向对象的阶段,兔C残篇在现阶段,先进行java SE的相关知识输出。
1.2 关于什么是数组
回到话题,什么是数组,我们知道基本数据类型有,byte short int long float double char boolean。我们在使用这些数据类型声明变量的时候,都是一个变量一个变量进行声明的,可是如果我们的变量都是相同类型的声明呢?比如 我们需要声明10个int类型的变量,那这时候我们一个一个进行声明么?不! 我们可以用到数组,数组就可以帮我们存储10个相同类型的数据,不在需要变量来一个一个存储。
1.3 图解 变量和数组的区别
//变量 声明在栈中
//数组 声明在栈中,并通过存储的地址值指向了堆,在堆中开辟了空间
2. 数组的定义语法
// 数据类型[] 数组名称 = new 数据类型[];
// 参上,是数组在java中的定义语法。其中的含义,我们接下来进行说明
// 我们可以先声明一个数组的类型
int[] array; //这就是声明了一个int类型的数组,名称为array
//但是还不能使用,因为我们还没有进行实例化,数组也是引用类型的。
array = new int[10]; //这里我们进行了数组的实例化,为其开辟了一块内存空间,而空间的状态为10个大小
//这里我们说明一个问题,我们经常看到其他人在声明数组的时候,会把[] 放在不同的位置,有的人放在了声明的类型后面,有的人写在了数组名后面,我们这里来进行说明
//[] 号的抒写位置,没有对错的区分
int[] array = new int[]; //括号放在数据类型的后面,是java 的标准写法。
int array[] = new int[]; //括号放在数组名称后面,是c和c++中的抒写风格。
//探其原因是因为,让早期的程序员更能适应新生的java 语言
//参上有一个问题,就是 12行 和 13行的代码,只是为了演示和说明,并没有设置初始化的容量大小,这里一定要切记,数组的初始化创建时候,必须声明初始容量的大小。
以上代码块中的注释,不难理解,可能发到博文上的注释,没有再typora上的视觉舒适,但是其注释内容容易理解,这里也只是说明了数组的创建语法,还有不同语法声明的区别。
文章进行到这里,我们的程序在编译器中的状态应该是,大家都声明出了数组,int array[] = new int[10];
但是数组怎么用,数组相关的理论知识呢? 我们接下来为大家逐一揭晓。
我们既然有了数组,那我们来先来看看数组在内存中的状态,在堆和栈中的状态。还有什么是堆,什么是栈?我们也先来一个铺垫。
3. 数组在内存中的状态
3.1 什么是栈
基本数据类型:当创建一个基本数据类型的变量时,会存放于栈中,而且会包含其具体的数据值。
引用数据类型:存放引用数据类型时,存放的是当前对象在堆里面的地址值
3.2 什么是堆
使用 new 关键字进行实例化的,都会进入到堆当中开辟内存空间,并且会有一个地址值,该地址值保存在栈中引用他的名称中。
3.3 图解 数组在内存中的状态
当我们编写代码时,比如声明一个数组,int array[];
这里我们并没有进行初始化,所以数组的默认值也是null,但是此时在内存中的状态呢?
因为只是声明了数组,并没有进行初始化,所以内存中也只是在栈中压入了一个array对象
而当我们为数组初始化之后,array = new int[5];
此时就会在堆中开辟空间,并且这块空间会被分割成5个小份,同时栈中的array也会指向这块内存空间
4. 数组的初始化
现在我们知道了数组的语法,就是怎么样声明一个数组,还知道了语法上的抒写区别,然后还有数组声明过程中在栈和堆中的状态,也知道了数组的类型是引用类型,最后还有数组和基本数据类型创建变量时 在内存当中的区别。
说完了这些,我们在聊一下数组的初始化方式,然后说完初始化方式,我们就开始聊数组上使用方面的话题。
4.1 静态初始化
//静态初始化,就是创建数组,在其实例化时为其定义好具体的存储数据值
int array[] = {1,2,3,4,5}; //这里就是用静态初始化,声明了一个空间大小为5且数据值为1,2,3,4,5的数组
4.2 动态初始化
//动态初始化,就是创建数组,在其实例化时,只声明该数组的大小
int array[] = new int[5]; //然后在使用的时候,在为其赋具体值
4.3 数组的默认初始化
int array[] = new int[5]; //动态初始化,也是默认的初始化方式
4.4 默认初始化的概念:
首先我们需要知道,我们的数组是引用类型的,它的元素相当于类的实例变量,然后也因此数组一旦分配内存空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化了。
4.5 数组元素的默认值
说完默认初始化的概念,我们在给大家科普一下数组的默认值,当我们创建一个int类型的数组,然后声明了数组空间的大小,如果此时声明是用的动态初始化的方式,声明之后没有赋值,那么没有赋值的元素默认值就为0。同样如果是String类型的话,那默认值就是null。
说到这还是补充一下吧,我们知道数组是引用类型的,那如果我们声明数组的时候,并没有进行实例化,那么数组的默认值是什么? 也为null!
4.6 隐式和显示的概念
这里的隐式就相当于我们在声明数组初始化容量大小时,没有给其中元素赋具体的值,然后会自动默认的有一个默认值,那显示是相对的,当我们需要使用存储数据,也就是给其中元素赋值时,就是显示的。
5. 数组的使用
5.1 关于数组使用上的概念认识
那说完了数组的基础概念,我们来说一下关于数组的使用:
这里我依然首先要知道的概念是,数组是有元素下标的,也就是初始化设置容量大小时,容量中的每个元素都有一逐一对应的下标值,那数组的下标是从0开始的,也就是说,比如设置的容量大小为5,那下标的顺序就是:0,1,2,3,4。那我们如何为其中的下标对应的元素赋值呢?
5.2 数组元素的赋值存储
int array[] = new int[5];
array[0] =10;
array[1] =20;
array[2] =30;
array[3] =40;
array[4] =50;
为什么标题写成了数组元素的赋值存储,因为存储的一组int数据类型中,其中的每一个元素,都相对于是一个变量。
5.3 数组元素中存储的数据取出
int array[] = new int[5];
//取出数组的元素我们可以直接利用下标值
//例如,我们想看到数组中下标元素为3 存储的数据是否为 40
System.out.println("值是多少呢? "+ array[3]); //指定取出
//如果我们想取出整个数组中的元素,我们可以使用for循环
for(int i =0; i<j array.length; i++){
//这里我们知道数组是引用类型的,那 array.length 就是代表数组的长度属性,要小于.length,因为我们刚刚已经探讨了数组的下标元素是从0开始的。
System.out.println(array[i]); //i就是对应的数组中的下标值,所以我们循环取值i
}
5.4 数组下标越界异常
上面的for循环中,我们已经写上了注释声明,那如果取值范围超出数组的下标了呢?
比如现在我们存储了5个元素,那对应的下标是:0,1,2,3,4
如果我们偏偏取值 array[5]; 就会报出错误信息:java.lang.ArrayIndexOutOfBoundException 提示我们出现了数组下标越界异常,一般出现此错误,就是因为我们的取值时超出了存储的范围。
6. 数组的特点
- 数组的长度是在创建时需要指定的,所以数组一旦被创建,就会在方法栈,方法堆中声明出对应的信息,比如声明在方法栈当中的对象名,存储用于指向方法堆当中具体实例化的空间内存地址值,比如方法堆中,会开辟出内存空间,空间中又会分割出小的空间来存储具体的值。
- 声明出来的数组一定是用于存储一组相同数据类型的数据。
- 数组可以存储基本数据类型,也可以是引用数据类型。只要是同一组相同的数据即可。
- 数组属于引用类型,数组也可以看成是对象,数组中的每个元素,相当于该对象的成员变量。
- 数组本身就是对象,java 的对象都是在堆中的,因此无论是用数组存储基本数据类型,还是引用数据类型,数组对象本身是在堆当中的。
- 数组是有序类型的,它的顺序是按照我们存储的顺序排序的。
7. 数组练习题
//在这里我们先定义一个数组,然后在定义一个变量,然后以下的demo都使用此数组和变量
int array[] = {1,2,3,4,5};
int number = array[0];
7.1 寻找数组中的最大值
for(int i =0; i< array.length; i++){
if(array[i] > number){
number = array[i];
}
}
System.out.println("数组中的最大值为:" +number);
7.2 补充:增强for循环
for(int arrays : array){ //第一个参数相当于数组的元素,第二个参数相当于数组
//增强for循环是 jdk1.5以后的新特性,它没有下标,所以适合打印输出,不适合操作元素
System.out.println(arrays);
}
//切记,增强for循环,没有下标,它无法操作元素
7.3 数组作为参数
另外,数组也可以作为方法的参数
代码示例:
public static void main(String[] args){
int array[] = {1,2,3,4,5};
printArray(array);
}
public static void printArray(int[] array){
for(int arrays : array){
System.out.println("元素值为;"+ arrays);
}
}
7.4 反转数组
这里我们写一个反转数组元素的democode,并把数组作为方法的返回值
public class demoFortoArray{
public stativ void main(String[] args){
//我们先在主程序中定义一个数组,用于后面反转的操作
int array[] ={1,2,3,4,5};
//这里调用反转数组元素的方法
int[] ints = toArray(array); //因为方法的返回值是 int[] 类型,所以会返回一个int类型的数组
}
//我们写一个反转数组元素的方法
public static int[] toArray(int[] array){
//我们定义一个用于存储反转后元素的数组,数组的长度等于 被传入到方法中的参数
int result[] = new int[array.length];
//int result[] ={array.length};
//i的下标,是被传入的数组下标,j的下标是我们方法体中数组的下标
for(int i = 0,j = result.length -1; i < array.length; i++ ,j--){
result[j] = array[i];
}
//这里进行返回
resurn result;
}
//定义一个打印输出数组元素的方法
public static void printArray(int[] array){
for(int i=0; i< array.length; i++){
System.out.println("当前元素的参数值为:"+array[i]);
}
}
}
8. 多维数组
多维数组的概念其实很好理解,比如我们现在的数组是一个数组,而这个数组的理论知识我们都已经掌握,那多维数组就是在一个数组的维度上嵌套了一层数组,也就是说一个普通数组的元素中,包含了下层维度的数组。
8.1 图解
8.2 多维数组的静态初始化
//我们用code写一个 三行两列的demo
int array[][] = {{1,2},{3,4},{5,6};
//如何取得二维数组中的元素呢?
//我们现在知道了二维数组,就是一维数组中的元素嵌套存储了一层数组的概念
//所以::
array[0]; //这是取得第一个数组的地址值
array[1]; //这是取得第二个数组的地址值
array[2]; //这是取得第三个数组的地址值
//我们也可以使用for循环,取得array[0]中的元素
//直接使用增强for,我们说过增强for适合打印输出,不适合操作元素
for(int arrays : array[0]){
System.out.println(arrays);
}
//使用普通for循环打印
for(int i =0; i< array[0].length;i++){
System.out.println(array[0][i]);
}
//那如果我们单独获取 二维数组维度中指定的值呢?
// 例如:需求(两个需求): 指定获取 1 指定获取 2
System.out.println(array[0][0]);
System.out.println(array[0][1]);
8.3 多维数组的动态初始化语法
int array[][] = new int[4][2];
//赋值动作
array[0][0] =1;
array[0][1] =2;
array[1][0] =3;
array[1][1] =4;
//以此类推
8.4 使用for循环输出数组中所有元素
int array[][] ={{1,2},{3,4},{5,6}};
//我们先用循环,设置出外层的数组
for(int i =0; i <array.length; i++){
//在用循环,设置出外层数组 元素的内层数组
for(int j =0; j <array[i].length; j++){ //注意内层循环的条件在外层循环之内
System.out.println(array[i][j]);
}
}
9. Java.util.Arrays 类
我们创建数组之后,有一个 .length 长度属性,来提供给我们操作数组的便捷,那如果我们想对数组有更多的操作呢?这时候 Arrays 工具类中,为我们提供出了一些相对的方法。这里我们举例某些常用方法。
9.1 .toString(); 方法
//我们用此方法可以得到数组中元素内容为String类型,然后用其输出。
int array[] = {1,2,3,4,5,6,7,8,9};
String tSarray = Arrays.toString(array);
System.out.println(tSarray);
9.2 .sort(); 方法
//我们可以使用sort方法按照升序顺序排序数组中的元素内容
int array[] = {9,3,6,1,2,8,4,7,10,5};
Arrays.sort(array);
//经过排序之后,我们在使用toString方法输出元素内容
String tSarray = Arrays.toString(array);
System.out.println(tSarray);
9.3 .fill 方法填充数组中的元素
//我们可以使用file方法,填充数组中的元素内容
int array[] = {1,2,3,4,5};
//例如我们都将其填充为0值
Arrays.fill(array,0);
String tSarray = Arrays.toString(array);
//它还提供了重载方法,指定填充某些元素
Arrays.fill(array,0,2,0); //0是起始下标位置,2为结束下标位置,0将其填充的目标值
10. 冒泡排序
冒泡排序,是最为出名的一种排序算法,总共有八大排序!
public class MpSort{
public static void main(String[] args){
int array[] = {10,6,2,9,4,3,8,1,7,5};
int[] ints = toMpArray(array);
String tsArray = Arrays.toString(ints);
System.out.println(tsArray);
}
public static int[] toMpArray(int[] array){
//空罐子
int temp =0;
//定义外层循环,用于控制冒泡的排序轮数
for(int i=0; i < array.length; i++){
//定义内层循环,用于比较和交换元素的位置
for(int j =0; j < array.length -1-i; j++){ //-i 是减去已比较过的元素 -1是下标的范围,不能超过数组的范围
//比较元素
if(array[j+1] < array[j]){ //j+1是下一个元素的下标
temp = array[j];
array[j] = array[j+1];
array[j+1] =temp;
}
}
}
return array;
}
}
//利用flag标志,控制程序,实现冒泡
public class MpSort{
public static void main(String[] args){
int array[] = {10,6,2,9,4,3,8,1,7,5};
int[] ints = toMpArray(array);
String tsArray = Arrays.toString(ints);
System.out.println(tsArray);
}
public static int[] toMpArray(int[] array){
//空罐子
int temp =0;
boolean flag =false; //通过flag标识位减少没有意义的比较
//定义外层循环,用于控制冒泡的排序轮数
for(int i=0; i < array.length; i++){
//定义内层循环,用于比较和交换元素的位置
for(int j =0; j < array.length -1; j++){ //-i 是减去已比较过的元素 -1是下标的范围,不能超过数组的范围
//比较元素
if(array[j+1] < array[j]){ //j+1是下一个元素的下标
temp = array[j];
array[j] = array[j+1];
array[j+1] =temp;
flag = true;
}
}
//如果定义的标志为false,说明没有走上面的循环,直接跳出
if(flag == false){
break;
}
}
return array;
}
}
11. 稀疏数组
稀疏数组,是一种数据结构。
11.1 需求
如果我们模拟一个五子棋的小程序,该程序具有存盘退出和续上盘的功能,而我们用数组进行存储。
我们想象一下五子棋棋盘的样子,那我们用0表示可以落子的棋盘格子,用1代表一个颜色的棋子,用2代表另一个颜色的棋子,那我们如何解决呢?
11.2 需求分析及解决方案
如果用二维数组的话,那也会存储很多个0,设置好棋盘的可以落子的格格,那这么多个0需要存储,我们有没有更好的解决办法呢?我们可以使用压缩算法,将数据量变少,也就是使用稀疏数组。
11.3 稀疏数组的概念
当一个数组中大部分元素为重复元素,或者为同一值的数组时,可以采用稀疏数组来保存该数组。
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
11.4 需求实现
public class xsArray{
public static void main(String[] args){
//1.创建一个二维数组, 11*11
//0:表示没有棋子,1:表示黑色棋子,2:表示白色棋子
int array[][] = new int[11][11]; //11行11列 棋盘格子
//坐标,1行2列的位置,落黑棋
array[1][2] = 1;
//坐标, 1行3列的位置,落黑棋
array[1][3] = 1;
//打印数组元素
for(int arrays: arrys){
for(int ints : arrays){
System.out.print(ints):
}
}
//转换为稀疏数组进行保存
//1.获取有效值的个数
int sum =0;
for(int i =0; i < 11; i++){
for(int j =0; j < 11; j++){
if(array[i][j]!=0){
sum++;
}
}
}
System.out.println("有效值的个数:"+sum);
//2.创建一个稀疏数组
int[][] arrays = new int[sum+1][3]; //[此处为有效元素的行数][此处为列的元素(行;列;值,所以是3)]
arrays[0][0] = 11; //行
arrays[0][1] = 11; //列
arrays[0][2] = sum; //值
//3.遍历二维数组,将非0的值,存放到稀疏数组当中
int count =0;
for(int i =0; i < array.length; i++){
for(int j = 0; j < array[i].length; j++){
count++;
//开始设置存放到稀疏数组
arrays[count][0] = array[i];
arrays[count][1] = array[j];
arrays[count][2] = array[i][j];
}
}
//4.输出稀疏数组
for(int i = 0 ; i < arrays.length; i++){
System.out.println(
arrays[i][0] +"\t" +
arrays[i][1] +"\t" +
arrays[i][j] +"\t"
);
}
//打印出来的值,是记录棋盘位置的棋子坐标
}
}
11.5 将稀疏数组还原成普通数组
//读取稀疏数组
int[] arrayt = new int[arrays[0][0],arrays[0][1]]; //行和列的坐标
//还原其中元素的值
for(int i = 1; i < arrays.length; i++){
arrayt[arrays[i][0]][arrays[i][1]] = arrays[i][2];
}
//打印还原后的元素值
for(int arrays: arryt){
for(int ints : arrays){
System.out.print(ints):
}
}