数组
# 数组(Array)的概述
是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
# 数组的常见概念
- 数组名
- 下标(或索引或角标)
- 元素
- 数组的长度
# 数组的特点
- 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
- 创建数组对象在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址
- 数组的长度一旦确定,就不能修改。
- 数组是有序排列的。可以通过下标的方式指定位置的元素。
还有一种不是连续空间的,叫列表,后面会提到。
数组的长度不能修改,如果要增加元素只能重新开辟内存空间,把原来的元素拷贝进去,追加新元素。
# 数组的分类
按照维度:
一维数组、二维数组、三维数组……
按照元素的数据类型分类:
基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)
# 一维数组
接下来我们将学习
- 一维数组的声明和初始化
- 如何调用数组的指定位置和元素
- 如何获取数组的长度
- 如何遍历数组
- 数组元素的默认初始化值
- 数组的内存解析
# 声明和初始化
# 语法格式
数据类型[] 变量名;
变量名 = new 数据类型[]{...}
2
数据类型[] 变量名 = new 数据类型[]{...}
数据类型 变量名[] = new 数据类型[]{...}
数据类型[] 变量名 = {...}
还有一种直接将引用指向某个引用
# 静态初始化
数组的初始化和数组的元素的赋值操作同时进行,称为数组的静态初始化。
声明:
int[] ids;
初始化:
ids = new int[]{123,124,125}
数组是引用数据类型,一定要用new
当然也可以声明和初始化一起写:
int[] ids = new int[]{123,124,125}
# 动态初始化
数组的初始化和数组的元素的赋值操作分开进行,称为数组的动态初始化。
String[] names = new String[5]
在实际业务场景中,有时候我们不能确定元素的赋值,所以需要先声明后面逻辑操作获得赋值。
# 错误的写法
int[] arr1 = new int[];
int[5] arr2 = new int[5];
int[] arr3 = new int[3]{1,2,3};
2
3
# 调用数组
如何调用数组的指定位置和元素?通过下标(索引或角标)的方式调用。
数组的下标(或索引) 是从0开始的,到数组的长度-1结束。
例如:
String[] names = new String[5]
names[0] = "名字一";
names[1] = "名字二";
names[2] = "名字三";
names[3] = "名字四";
names[4] = "名字五";
2
3
4
5
6
当你尝试操作第6个下标,会发生数组越界
names[5] = "名字六";
编译虽然能通过,但是程序会报java.lang.ArrayIndexOutOfBoundsException
因为在初始化时,数组的长度只有5,当你去访问第6个,自然会数组越界。
# 数组长度
每个数组都有个length
属性,例如上面案例中,可以这样访问:
System.out.println(names.length)
将会返回5。
# 遍历数组
还是用上面names
数组:
for(int i=0;i<names.length-1;i++){
System.out.println(names[i]);
}
2
3
# 默认初始化值
如果数组初始化时,元素不指定值,会根据数组类型产生默认值。
- 整型,默认值为:0
- 浮点型,默认值为:0.0
- char型,默认值为:0或‘\u0000’,而非‘0’
- boolean型,默认值为:false
- 如果元素是引用数据类型,默认值为:null
数组元素是int
类型
package arrayTest;
public class Demo1 {
public static void main(String[] args) {
int[] arr = new int[4];
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
2
3
4
5
6
7
8
9
10
0
0
0
0
2
3
4
数组元素是short
类型
short[] arr2 = new short[4];
for (int i = 0; i < arr2.length; i++) {
System.out.println(arr2[i]);
}
2
3
4
0
0
0
0
2
3
4
数组元素是float
类型
float[] arr3 = new float[4];
for (int i = 0; i < arr3.length; i++) {
System.out.println(arr3[i]);
}
2
3
4
0.0
0.0
0.0
0.0
2
3
4
数组元素是char
类型
char[] arr4 = new char[4];
for (int i = 0; i < arr4.length; i++) {
System.out.println("---" + arr4[i] + "***");
if(arr4[i] == 0){
System.out.println("默认是0或‘\\u0000’,而非‘0’");
}
}
2
3
4
5
6
7
--- ***
默认是0或‘\u0000’,而非‘0’
--- ***
默认是0或‘\u0000’,而非‘0’
--- ***
默认是0或‘\u0000’,而非‘0’
--- ***
默认是0或‘\u0000’,而非‘0’
2
3
4
5
6
7
8
数组元素是boolean类型
boolean[] arr5 = new boolean[4];
for (int i = 0; i < arr5.length; i++) {
System.out.println(arr5[i]);
}
2
3
4
false
false
false
false
2
3
4
数组元素是引用数据类型
String[] arr6 = new String[4];
for (int i = 0; i < arr6.length; i++) {
System.out.println(arr6[i]);
if (arr6[i] == null) {
System.out.println("引用数据类型,默认值为:null");
}
}
2
3
4
5
6
7
null
引用数据类型,默认值为:null
null
引用数据类型,默认值为:null
null
引用数据类型,默认值为:null
null
引用数据类型,默认值为:null
2
3
4
5
6
7
8
# 数组的内存解析
其实new关键字,就是面向对象的用法。
- 内存的结构
内存的规范首次是JVM的书籍中提到,不同JDK中当中,内存的结构稍微有所区别。
# 简化图
内存中结构中,可以将内存划分为:
栈(stack):局部变量
堆(heap):new出来的结构:对象、数组
方法区(method area)
- 常量池
- 静态域
代码解析:
int[] arr = new int[]{1,2,3};
# 练习1
公寓合租招租4个月,1550元/月(水电煤公摊,网费35元/月),由于屋内均是IT人士,所以他的联系方式为:
8,2,1,0,3
2,0,3,2,4,0,1,3,2,2,3
2
那么他的联系方式是?
package arrayTest;
public class Demo2 {
public static void main(String[] args) {
int[] arr = new int[]{8, 2, 1, 0, 3};
int[] index = new int[]{2, 0, 3, 2, 4, 0, 1, 3, 2, 2, 3};
String tel = "";
for (int i = 0; i < index.length; i++) {
tel += arr[index[i]];
}
System.out.println("联系方式为:" + tel);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
联系方式为:18013820110
# 练习2
package arrayTest;
import java.util.Scanner;
public class Demo3 {
public static void main(String[] args) {
int max = 0;
Scanner scan = new Scanner(System.in);
// 获取学生人数
System.out.println("请输入学生的人数:");
int nums = scan.nextInt();
int[] scores = new int[nums];
//获取学生分数
System.out.println("请以此输入" + nums + "个学生的成绩:");
for (int i = 0; i < scores.length; i++) {
//给数组中的元素赋值
scores[i] = scan.nextInt();
if (max < scores[i]) {
//获取最高分
max = scores[i];
}
}
//遍历数组,评级
for (int i = 0; i < scores.length; i++) {
String rate = "";
if (scores[i] >= max - 10) {
rate = "A";
} else if (scores[i] >= max - 20) {
rate = "B";
} else if (scores[i] >= max - 30) {
rate = "C";
} else {
rate = "D";
}
System.out.println("学号:" + i + "的学生成绩为:" + scores[i] + ",评级为:" + rate);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 多维数组
Java语言里提供了支持多维数组的语法。多维数组又以二维数组居多。如果说可以把一堆数组当成几何中的线性图形,那么二维数组就相当于一个表格。对于二维数组的理解,我们可以看成一维数组又作为另一对堆一维数组的元素而存在。其实,从数组底层的运行机制来看,其实没有多维数组。
接下来我们将学习
- 二维数组的声明和初始化
- 如何调用数组的指定位置和元素
- 如何获取数组的长度
- 如何遍历数组
- 数组元素的默认初始化值
- 数组的内存解析
# 声明和初始化
# 语法格式
数据类型[][] 变量名 = new 数据类型[][]{{……},{……},{……}}
数据类型[] 变量名[] = new 数据类型[][]{{……},{……},{……}}
数据类型 变量名[][] = new 数据类型[][]{{……},{……},{……}}
数据类型[][] 变量名 = {{……},{……},{……}}
# 静态初始化
//一维数组
int[] arr = new int[]{1,2,3};
//二维数组
int[][] arr2 = new int[][]{{1,2,3},{4,5},{7,8,9}};
2
3
4
# 动态初始化
String[][] arr3 = new String[3][2];
String[][] arr4 = new String[3][];
2
# 错误的写法
String[][] arr5 = new String[][];
String[][] arr6 = new String[][3];
String[][] arr7 = new String[4][3]{{1,2,3},{4,5},{7,8,9}};
2
3
# 调用数组
//二维数组
int[][] arr = new int[][]{{1,2,3},{4,5},{7,8,9}};
//访问外层元素
System.out.println(arr[0]);
//访问内层元素
System.out.println(arr[0][1]); //2
2
3
4
5
6
同样如果数组下标越界,也会报java.lang.ArrayIndexOutOfBoundsException
# 数组长度
//二维数组
int[][] arr = new int[][]{{1,2,3},{4,5},{7,8,9}};
//外层元素长度
System.out.println(arr.length);//3
//内层元素长度
System.out.println(arr[1].length);//2
2
3
4
5
6
# 遍历数组
关于多维数组的遍历,二维就二层循环,n层就n层循环。
package arrayTest;
public class Demo1 {
public static void main(String[] args) {
int[][] arr = new int[][]{{1,2,3},{4,5},{7,8,9}};
for (int i = 0; i < arr.length; i++) {
for(int j=0;j<arr[i].length;j++){
System.out.print(arr[i][j]);
}
System.out.println();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 默认初始化值
二维数组分为外层元素和内层元素。
外层元素是一个对象,内层元素是才是具体值。
int[][] arr = new int[4][3];
System.out.println(int[0]);//内存地址
System.out.println(int[0][0])//0 一个整形
2
3
外层元素是指向一个对象的内层地址,代表一个对象。
内层元素是指向具体值。具体值的默认值跟一维数组的一样。
int[][] arr = new int[4][];
外层元素是指向一个对象的内层地址,但是并没有指向任何对象,所以是null。
内层元素还没被创建,所以也不能访问。
# 内存解析
二维数组的内存解析
# 练习1
通过图像可知,存在一个多维数组
package arrayTest;
import java.util.Scanner;
public class Demo4 {
public static void main(String[] args) {
int total = 0;
int[][] arr = new int[][]{{3, 5, 8}, {12, 9}, {7, 0, 6, 4}};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
total += arr[i][j];
}
}
System.out.println(total);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
结果是54
# 练习2
a no,
b yes,
c no
d no
e yes
f no
# 练习3
package arrayTest;
public class Demo5 {
public static void main(String[] args) {
int[][] arr = new int[10][];
for (int i = 0; i < arr.length; i++) {
System.out.print("[" + 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 if (i > 1) {
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
}
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[0] 1
[1] 1 1
[2] 1 2 1
[3] 1 3 3 1
[4] 1 4 6 4 1
[5] 1 5 10 10 5 1
[6] 1 6 15 20 15 6 1
[7] 1 7 21 35 35 21 7 1
[8] 1 8 28 56 70 56 28 8 1
[9] 1 9 36 84 126 126 84 36 9 1
2
3
4
5
6
7
8
9
10
拓展题:
package arrayTest;
public class Demo6 {
public static void main(String[] args) {
int[] arr = new int[6];
int count = 0;
for (int i = 0; i < arr.length; i++) {
while (true) {
boolean flag = true;
int number = (int) (Math.random() * 30 + 1);
System.out.print("\t随机值:" + number);
for (int j = 0; j < count; j++) {
if (arr[j] == number) {
System.out.print("\t已存在!将重新生成!");
flag = false;
}
}
if(flag){
System.out.println("\t插入成功");
arr[i] = number;
count++;
break;
}
}
}
System.out.print("最后生成的值如下:");
for (int i : arr) {
System.out.print(i + " ");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 数组中涉及的常见算法
- 数组元素的赋值(杨辉三角、回型数等)
- 求数值型数组中元素的最大值、最小值、平均数、总和等。
- 数组的复制、反转、查找(线性查找、二分法查找等)
- 数组元素的排序算法
# 数值型相关练习
求数值型数组中元素的最大值、最小值、平均数、总和等。
定义一个int型的一维数组,包含10个元素,分别赋随机整数,然后求出所有元素的最大值,最小值,和值,平均值,并输出在控制台。
要求:所有随机数都是两位数。
实现代码:
package arrayTest;
/**
* 求数值型数组中元素的最大值、最小值、平均数、总和等。
* <p>
* 定义一个int型的一维数组,包含10个元素,分别赋随机整数,然后求出所有元素的最大值,最小值,和值,平均值,并输出在控制台。
* <p>
* 要求:所有随机数都是两位数。
*/
public class Demo9 {
public static void main(String[] args) {
int[] arr = new int[10];
//最大值
int max = 0;
//最小值
int min = 100;
//和值
int total = 0;
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 90 + 10);
System.out.print(arr[i] + "\t");
//最大值
if (max < arr[i]) {
max = arr[i];
}
//最小值
if (min > arr[i]) {
min = arr[i];
}
//和值
total += arr[i];
}
System.out.println();
//平均值
double avg = (double) total / arr.length;
System.out.println("最大值为:" + max);
System.out.println("最小值为:" + min);
System.out.println("和值为:" + total);
System.out.println("平均值为" + avg);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 数组的复制
使用简单数组
- 创建一个名为ArrayTest的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
- 使用大括号{},把array1初始化8个素数:2,3,5,7,11,13,17,19.
- 显示array1的内容。
- 赋值array2变量等于array1,修改array2中的偶索引元素,时期等于索引值(如array[0]=0,array[2]=2)。打印出array1。
思考:array1和array2是什么关系?
拓展:修改题目,实现array2对array1数组的复制。
package arrayTest;
/**
* 使用简单数组
* <p>
* 1. 创建一个名为ArrayTest的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
* 2. 使用大括号{},把array1初始化8个素数:2,3,5,7,11,13,17,19.
* 3. 显示array1的内容。
* 4. 赋值array2变量等于array1,修改array2中的偶索引元素,时期等于索引值(如array[0]=0,array[2]=2)。打印出array1。
* <p>
* 思考:array1和array2是什么关系?
* <p>
* 拓展:修改题目,实现array2对array1数组的复制。
*/
public class ArrayTest {
public static void main(String[] args) {
int[] array1, array2;
array1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + "\t");
}
array2 = array1;
for (int i = 0; i < array2.length; i++) {
if (i % 2 == 0) {
array2[i] = i;
}
}
System.out.println();
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + "\t");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
结果:
2 3 5 7 11 13 17 19
0 3 2 7 4 13 6 19
2
思考:array1和array2是什么关系?
array1和array2地址值相同,都指向了堆空间的唯一的一个数组实体。所以修改array2时,实际上是修改内存中的实际值,当修改完毕后,array1也是指向这个内存,所以它打印的值也显示更改了。
赋值array2变量等于array1
数组不能这样赋值,如果要复制一份,需要重新在内存中开辟新的空间,重新新建数组实体。
array2 = new int[array1.length];
for (int i = 0; i < array1.length; i++) {
array2[i] = array1[i];
}
2
3
4
完整代码:
package arrayTest;
/**
* 使用简单数组
* <p>
* 1. 创建一个名为ArrayTest的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
* 2. 使用大括号{},把array1初始化8个素数:2,3,5,7,11,13,17,19.
* 3. 显示array1的内容。
* 4. 赋值array2变量等于array1,修改array2中的偶索引元素,时期等于索引值(如array[0]=0,array[2]=2)。打印出array1。
* <p>
* 思考:array1和array2是什么关系?
* <p>
* 拓展:修改题目,实现array2对array1数组的复制。
*/
public class ArrayTest {
public static void main(String[] args) {
int[] array1, array2;
array1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + "\t");
}
/**
* array1和array2地址值相同,都指向了堆空间的唯一的一个数组实体。
* 所以修改array2时,实际上是修改内存中的实际值,
* 当修改完毕后,array1也是指向这个内存,所以它打印的值也显示更改了。
*/
// array2 = array1;
//复制数组
array2 = new int[array1.length];
for (int i = 0; i < array1.length; i++) {
array2[i] = array1[i];
}
//修改array2中的偶索引元素,时期等于索引值(如array[0]=0,array[2]=2)
for (int i = 0; i < array2.length; i++) {
if (i % 2 == 0) {
array2[i] = i;
}
}
System.out.println();
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + "\t");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
输出结果:
2 3 5 7 11 13 17 19
2 3 5 7 11 13 17 19
2
# 数组的反转操作
方式一:
package arrayTest;
public class Demo10 {
public static void main(String[] args) {
String[] arr = new String[]{"JJ", "DD", "MM", "BB", "GG", "AA"};
//数组的反转
for (int i = 0; i < arr.length / 2; i++) {
String temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = temp;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
方式二:
//方式二:
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
String temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
2
3
4
5
6
# 数组中常见的异常
这两种异常,编译时都不会报错。
- 数组角标越界的异常:
ArrayIndexOutOfBoundsException
- 空指针异常:
NullPointerException
# 数组脚标越界异常(ArrayIndexOutOfBoundsException)
访问到了数组中的不存在的脚标时发生。
int[] arr= new int[2];
System.out.println(arr[2]);
System.out.println(arr[-1]);
2
3
# 空指针异常(NullPointerException)
arr引用没有指向实体,却在操作实体中的元素时。
int[] arr= null;
System.out.println(arr[0]);
2