数组
0. 前置知识(ps:先稍微了解一下jvm)
-
虚拟机栈
- 存储局部变量
-
堆
- 此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。
-
C:方法区
- 它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据
-
D:本地方法栈
- 本地方法栈(NativeMethodStacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务
-
E:寄存器
- 程序计数器(ProgramCounterRegister)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
1 数组概述
数组,是一组数据的集合,数组中的每个数据被称为元素。
如果有五个数据1 2 3 4 5,需要去接收、保存、操作这些数据,那么需要五个变量:
int a1 = 1;
int a2 = 2;
int a3 = 3;
int a4 = 4;
int a5 = 5;
除此之外,还可以选择使用一个组数来保存这五个数据:
int[] arr = {1,2,3,4,5};
//这里就是使用了一个数组,来保存这五个数据
在java中,数组也是对象。数组中的元素可以是任意类型(基本类型或引用类类型),但同一个数组里只能存放类型相同的元素。
main方法的参数,就是一个String类型的数组:
public static void main(String[] args){ //这个参数args的类型是字符串数组
//控制循环输出的次数
int num = 1;
//如果main方法的参数args有接收到参数值的话
if(args.length > 0){
//把接收到的值转换为int类型,并赋值给变量num
num = Integer.parseInt(args[0]);
}
//循环输出hello,默认输出的次数为1,如果用户给main方法传参了,则按照用户的要求的次数进行输出
for(int i =0;i<num;i++){
System.out.println("hello");
}
}
args.length 是获取这个数组对象中的元素个数
args[0] 是获取这个数组中,第一个元素的值
Integer.parseInt()方法是将一个字符串转换为int数字,例如,“109” ==> 109
使用java命令运行类中main方法,并给其传参:
java com.briup.day05.PrintHello 20
这里的这个20就是给类中的main方法进行参数的,这个参数会存放到args这个数组对象中。
总结:
- 数组是存储同一种数据类型多个元素的集合。也可以看成是一个容器。
- 数组既可以存储基本数据类型,也可以存储引用数据类型
2 数组类型
一般情况下,java中的类(class),是需要提前把代码写好,然后编译成class文件,再加载到内存中,最后在程序中就可以使用到这个类了。
而数组和类不同,数组是使用当前已经存在的某一个类型(任意类型都可以),然后再这个类型后面加上一对中括号([]),这时就组成一个了新的类型:数组类型
例如,任意类型 + [] = 相应的数组类型
byte + [] ---> byte[]
short + [] ---> short[]
int + [] ---> int[]
long + [] ---> long[]
float + [] ---> float[]
double+ [] ---> double[]
char + [] ---> char[]
boolean+[] ---> boolean[]
String+ [] ---> String[]
Action+ [] ---> Action[] //Action假设是一个接口
int[] + [] ---> int[][] //一维数组+[] 变为二维数组
数组中可以存放一组数据,要求这一组数据的类型是一样(或者兼容的,兼容就表示可以自动转换)。
例如,int类型数组(int[])中,也是可以存放byte数据的,因为byte可以自动转换为int(兼容)
3 数组变量
先使用一个已有的类型,然后加上中括号,组合成一个数组类型,然后再声明这个数组类型的变量
俩种方式声明一个数组类型的变量:
-
int[] a;
-
int a[];
推荐使用第一种声明的方式
其他一些声明数组变量的示例:
String[] str;
Student[] stus;
Object[] objs;
long[] arr;
int[][] arrOfArr;
数组类型的变量,也是引用类型变量,简称引用,它是可以指向对象的(数组对象)
4 数组对象
使用new关键字,来创建数组对象,中括号里面的数字,就是数组的长度。
int[] a = new int[4];//数组对象a中,最多存放4个int类型的数据
String[] s = new String[2];//数组对象s中,最多存放2个String类型的数据
char[] c = new char[1];//数组对象c中,最多存放1个char类型的数据
数组对象,在内存中,就是一块连续的内存空间,在这个连续的空间中,可以存放多个类型相同的数据。
思考,数组对象中都有哪些属性和方法可以使用?
数组对象中只有一个属性(length),表示数组的长度。
数组对象中的方法,只有从父类型Object中继承过来的方法。
除此之外,数组对象中就没有其他属性和方法了。
5 数组长度
数组对象的长度:
- 数组长度,是指在一个数组对象中,最多可以存放多少个同一类型的数据
- 数组长度,必须在创建数组对象的时候就明确指定
- 数组长度,一旦确定,就无法再改变
- 数组长度,可以为0,但是不能为负数
例如,错误的一些情况
//编译报错,因为没指定数组的长度
byte[] b = new byte[];
//编译通过,但是运行报错,因为数组的长度不能为负数
byte[] b = new byte[-1];
例如,使用length属性,获取数组对象的长度
int[] a = new int[4];
System.out.println(a.length);
6 数组下标
数组对象在JVM的堆区中是一块连续的内存空间,并允许使用下标,设置或获取每一个内存空间的值
数组的下标的区间为,[0,arr.length-1],假如数组的长度为length的话,那么数组下标的最小值为0,数组下标的最大值就是length-1
如果使用下标访问数组中元素的时候,下标的值超出了其可用范围,那么运行就会报错:
int[] a = new int[4];//数组长度为4,那么其下标就是0~3
a[4] = 359;//这句代码运行时报错,下标超出了最大范围
可以通过数组下标,给数组某个下标位置上赋值:
int[] a = new int[4];
a[0] = 337;
a[1] = 340;
a[2] = 348;
a[3] = 352;
可以通过数组下标,获取数组某个下标位置上的值:
int[] a = new int[4];
System.out.println(a[0]):
int num = a[0]+a[1]+a[2]+a[3];
System.out.println(num);
可以结合循环来进行赋值或者取值:
int[] a = new int[4];
//i从0开始,到a.lenght-1,那么i的取值刚好是数组a的下标可用范围
for(int i=0;i<a.length;i++){
a[i] = 10+i;
}
//获取数组中每个下标的值,并且输出
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
7 默认值
一个数组对象在创建的时候,需要指定数组长度,表示数组中最多存放的元素个数,并且在数组对象创建完成之后,数组中每一个元素位置上,就已经有了相应的默认值,这个默认值和数组的类型有关。
//byte、short、int、long类型数组中的默认值为 0
//例如,
int[] a = new int[4];//默认4个数据全是0
//float、double类型数组中的默认值为 0.0
//例如,
double[] d = new double[4];//默认4个数据全是0.0
//boolean类型数组中的默认值为 false
//例如,
boolean[] d = new boolean[4];//默认4个数据全是false
//char类型数组中的默认值为 '\u0000'
//例如,
char[] d = new char[4];//默认4个数据全是'\u0000'
//引用类型数组中的默认值为 null
//例如,
String[] d = new String[4];//默认4个数据全是null
8 数组初始化
数组开辟连续的内存空间,并为每个数组元素赋予值
数组对象的初始化形式有多种,每一种都要求大家掌握
下面以创建int类型数组对象进行说明
-
数组动态始化
动态初始化 只指定长度,由系统给出初始化值
int[] arr = new int[5];
创建数组对象,没有还没有给其赋值,都是默认值 -
静态初始化
给出初始化值,由系统决定长度
int[] arr = new int[]{1,3,5,7,9};
创建数组对象的同时,并赋值int[] arr = {1,3,5,7,9};
java提供了创建数组对象的简便形式int[] arr;
arr = new int[]{1,3,5,7,9};
显示声明数组类型变量,然后创建对象,并赋值
以下一些错误的数组创建方式:
错误方式一:
int[] a = new int[3]{1,2,3};
错误方式二:
int[] a;
a = {1,2,3};
9.数组常见的问题
-
ArrayIndexOutOfBoundsException:数组索引越界异常
访问了不存在的索引
-
NullPointerException:空指针异常
//数组已经不在指向堆内存了。而你还用数组名去访问元素 int[] arr = {1,2,3}; arr = null; System.out.println(arr[0]);
10. 数组遍历
-
可以采用循环或者是增强for循环遍历
int[] arr = {1,4,4,5}; for(int ar : arr) { System.out.println(ar); }
9 数组拷贝
数组对象的长度确定之后便不能修改,但可以通过复制数组的内容变通实现改变数组长度
在java.lang.System类中提供一个名为arraycopy的方法可以实现复制数组中元素的功能
//该方法的声明
public static void arraycopy(Object src,
int srcPos,
Object dest,
int destPos,
int length)
//参数1,需要被复制的目标数组
//参数2,从目标数组的哪一个位置开始复制
//参数3,需要把数据复制到另外一个新的数组中
//参数4,把数据复制到新数组的时候,需要把数据从什么位置开始复制进去
//参数5,复制的目标数组的长度
例如,写一个方法,接收一个数组对象,把这个数组对象的长度扩大到原来的2倍并返回。
public int[] test(int[] a){
int[] b = new int[a.length*2];
System.arraycopy(a, 0, b, 0, a.length);
return b;
}
10 工具类
由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
java.util.Arrays类,是JAVASE API中提供给我们使用的一个工具类,这个类的作用就是在代码中辅助我们对数组对象进行操作的。里面有很多静态方法可以直接调用,主要的功能就是对数组对象进行操作,例如排序、查询、复制、填充数据等等。
Arrays中的常用方法:
-
toString方法
可以把一个数组变为对应的String形式 -
copyOf方法
可以把一个数组进行复制
该方法中也是采用了arraycopy方法来实现的功能 -
copyOfRange方法
也是复制数组的方法,但是可以指定从哪一个下标位置开始复制
该方法中也是采用了arraycopy方法来实现的功能 -
sort方法
可以对数组进行排序 -
binarySearch方法
在数组中,查找指定的值,返回这个指定的值在数组中的下标,但是查找之前需要在数组中先进行排序,可以使用sort方法先进行排序 -
equals方法
arr1.equals(arr2)
euqals方法比较的是两个数组的地址,相同则返回true,否则是false
如果是Arrays.equals(arr1,arr2),这个工具类的方法比较的是两个数组中的元素的值和元素的顺序,如果都相同就返回true,否则返回false -
fill
可以使用一个特定的值,把数组中的空间全都赋成这个值 -
asList
可以把一组数据,封装到一个List集合中,并且把list集合对象返回。
例如,
//注意,数组长度一旦确定,就不能再修改了。
//我们只是创建了一个新的数组,并且把老数组中的数据复制到了新数组中,并且把新数组返回给用户,这个效果让人感觉上像是老的数组的长度变长了,但其实并没有。
int[] a = {1,3,5,2,6,8};
System.out.println(Arrays.toString(a));
a = Arrays.copyOf(a,10);
System.out.println(Arrays.toString(a));
Arrays.sort(a);
System.out.println(Arrays.toString(a));
int index = Arrays.binarySearch(a,5);
System.out.println(index);
int[] arr1 = {1,2,3};
int[] arr2 = {3,2,1};
Arrays.sort(arr1);
Arrays.sort(arr2);
System.out.println(Arrays.equals(arr1,arr2));
Arrays.fill(a,100);
System.out.println(Arrays.toString(a));
11 案例
例如,实现一个方法,参数是int[],该方法可以计算出数组中所有数据的平均值,并且把结果返回
public double test(int[] arr){
int length = arr.length;
int sum = 0;
for(int i=0;i<length;i++){
sum = sum + arr[i];
}
return sum/length;
}
例如,实现一个方法,参数是int[],该方法可以计算出数组中所有数据的最大值,并且把结果返回。
public int test(int[] arr){
//max变量中的值,表示当前找到的最大值
//假设数组中下标为0的位置是最大值
//这步假设赋值的目的就是为了给局部变量max一个初始值
int max = arr[0];
for(int i=1;i<arr.length;i++){
if(arr[i]>max){
max = arr[i];
}
}
return max;
}
数组元素反转(就是把元素对调)
public static void reverseArray(int[] arr) {
for (int i = 0;i < arr.length / 2 ; i++) {
//arr[0]和arr[arr.length-1-0]交换
//arr[1]和arr[arr.length-1-1]交换
//arr[2]和arr[arr.lentth-1-2]
//...
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
}
12 二维数组
如果把普通的数组(一维数组),看作一个小盒子的话,盒子里面可以存放很多数据,那么二维数组就是想一个大点的盒子,里面可以存放很多小盒子(一维数组)
12.1 理解二维数组
例如,一维数组中存放的是普通数据
例如,二维数组中存放的是一维数组对象
可以看出,二维数组里面存放的是一维数组(array Of array)
例如,
int + [] = int[] ,所以一维数组int[]中存放的是数据是int类型数据
int[] + [] = int[][] ,所以二维数组int[][]中存放的是数据是int[]类型数据,也就是一维数组对象
任何一个一维数组,再加一对中括号[],就变成了二维数组
例如,
//三个一维数组
int[] a1 = {1,2,3};
int[] a2 = {2,3,4};
int[] a3 = {3,4,5};
//二维数组中,存放三个一维数组对象
int[][] arr = {
a1,
a2,
a3
};
//等价于上面的代码
int[][] arr = {
{1,2,3},
{2,3,4},
{3,4,5}
};
12.2 声明和创建
//第一个中括号中的4,代表这个二维数组对象里面,最多可以存放4个一维数组
//第二个中括号中的3,代表这个二维数组中,每个存放的一维数组对象,都可以存放存放3个int数据
int[][] a = new int[4][3];
//这句代码等价于以下代码
int[][] a = new int[4][];
a[0] = new int[3];
a[1] = new int[3];
a[2] = new int[3];
a[3] = new int[3];
//这句代码等价于以下代码
int[][] a = {
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0}
};
可以把这个二维数组,理解为一栋大厦,共有4层楼,每层楼有3间房,每个房间可以存放一个int数据,现在每个房间的默认值都是0
思考:把二维数组看作一栋大厦的话,假设共4层楼,那么每层楼的房间数是否可以不同?
每层楼的房间数,可以不同,使用代码可以如下表示:
int[][] a = new int[4][]; //共4层楼
//每次楼的房间数不同
a[0] = new int[3];
a[1] = new int[5];
a[2] = new int[2];
a[3] = new int[4];
//也可以这样来表示
int[][] a = {
{1,2,3},
{1,2,3,4,5},
{1,2},
{1,2,3,4}
};
12.3 赋值
例如,
int[][] a = new int[3][];//三层楼
a[0] = new int[1];//第一层楼,1间房
a[1] = new int[2];//第二层楼,2间房
a[2] = new int[3];//第三层楼,3间房
a[0][0] = 10; //第一层楼,第一间房,存放数据
a[1][0] = 20; //第二层楼,第一间房,存放数据
a[1][1] = 20; //第二层楼,第二间房,存放数据
a[2][0] = 30; //第三层楼,第一间房,存放数据
a[2][1] = 30; //第三层楼,第二间房,存放数据
a[2][2] = 30; //第三层楼,第三间房,存放数据
12.4 取值
String[][] str = {
{"test1"},
{"hello1","hello2"},
{"world1","world2","world3"}
};
//循环三次,因为str中有三个元素
//只不过这三个元素每一个都是个一维数组
for(int i=0;i<str.length;i++){
//第一个一维数组中有1个元素,元素是String类型的
//第二个一维数组中有2个元素,元素是String类型的
//第三个一维数组中有3个元素,元素是String类型的
//所以内层循环每次打印的次数是不一样的
for(int j=0;j<str[i].length;j++){
System.out.println(str[i][j]);
}
}
12.5 案例
使用二维数组,打印输出杨辉三角,效果如下:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
代码实现:
//参数line,表示需要输出的行数
public void test(int line){
int[][] arr = new int[line][];
//根据规律,构造出二维数组并且赋值
for(int i=0;i<arr.length;i++){
arr[i] = new int[i+1];
//循环给二维数组中的每个位置赋值
for(int j=0;j<arr[i].length;j++){
if(j==0 || j==arr[i].length-1){
arr[i][j] = 1;
}
else{
//除了下标中的0和最后一个,其他的元素都具备相同的规律
//这个位置的值=上一层和它相同下标的值+前一个元素的值
arr[i][j] = arr[i-1][j] + arr[i-1][j-1];
}
}
}
//把赋值完成的二维数组按要求进行输出
for(int i=0;i<arr.length;i++){
//控制每行开始输出的空格
for(int j=0;j<(arr.length-i-1);j++){
System.out.print(" ");
}
//控制输出二维数组中的值,记得值和值之间有空格隔开
for(int k=0;k<arr[i].length;k++){
System.out.print(arr[i][k]+" ");
}
//当前行输出完,再单独输出一个换行
System.out.println();
}
}