一.单元概述
在Java语言中,有一种特殊的引用类型叫数组,它在概念上属于类的范畴,但处理的方式又与类和对象不完全相同,本章节主要重点介绍数组的定义和创建以及常见的应用。
二、教学重点与难点
(1) 一维数组;
(2) 数组的异常;
(3) 数组的操作。
难点:
(1) 多维数组的内存模型;
(2) 数组的复制和排序。

4.1一维数组

数组是相同类型的数据按照顺序组合后的一种引用类型。数组可以看成是多个相同类型数据的组合,实现对这些数据的统一管理。数组的长度一旦确定后就不能更改,因此它是一个固定长度的结构。数组结构中每个存储的数据叫数组元素,数组元素由索引来定位。索引(或叫数组下标)是指当前元素相对于第一个元素的位移,因此第一个元素的下标就是0,第二个元素的下标就是1,以此类推。

4.1.1数组的定义

数组声明的语法:
数据类型[] 数组名; 或者
数据类型 数组名[];

例如:char[] c; String strs[];

提示: 方括号“[]”既可以放在数据类型的后面,也可以放在数组名的后面,两者都可以标识一个数组的声明,但是第一种写法可读性更好些。

数组声明后,Java虚拟机就会给数组分配存储空间,情况如下图所示:

04.数组 - 图1

图4.1数组声明后的内存情况示意图

数组在声明后,但是没有创建前,并没有给它分配具体的内存空间,所以这个时候访问数组会出现NullPointerException异常。我们需要在访问数组前创建数组,确定数组的长度,以便能为数组分配内存空间。

4.1.2数组的创建

数组的长度在创建时指定,并且一旦指定就不能更改。创建数组有两种方式:

1.静态初始化
这种方式在声明的同时就直接初始化数组,同时也创建了数组空间。如:
int[] m = {3,75,234,41,16};
上面的语句运行后,就会创建一个数组m,这个数组的长度是5,他有5个元素,分别是3,75,234,41,16。

2.动态初始化
这种创建方式的语法是:
数组名 = new 数组类型[数组长度];
如:
char[] c = new char[128];
String[] strs = new String[10];
double[] incoming = new double[73];

数组有默认值,对于数字类型,默认值为0,对于字符型,默认值为‘\u0000’,对于布尔类型,默认值为false, 对于对象类型,默认值为null

04.数组 - 图2

图4.2数组创建后的内存情况示意图

4.1.3数组的内存模型

数组是存储多个相同类型数据的对象。数组的所有元素保存在堆内存中。 创建一个数组就是在堆中创建一个数组对象。数组创建后立即拥有默认值。索引从0开始。连续分配
例如:
int[ ] array ;
array = new int [4] ;

数组的内存模型如下图4.3所示。 04.数组 - 图3

图4.3数组的内存模型示意图

4.1.4数组的初始化

数组的元素通过下标来标识,下标表示当前元素相对于第一个元素的位移,因此从0开始。比如name[0]就表示name数组的第1个元素,name[4]就表示name数组的第5个元素。其中方括号“[]”中的整数就是下标(又叫索引),要注意下标的范围从0~数组长度-1,如果访问超过范围的下标,就会发生ArrayIndexOutOfBoundsException下标越界异常。

数组的初始化的两种方式如下图4.4所示。
04.数组 - 图4

图4.4数组的初始化

例4.1 创建数组并初始化。

  1. class Array_sample1 {
  2. public static void main(String args[]){
  3. int a[];
  4. a = new int[5];
  5. for(int i=0;i<5;i++){
  6. a[i] = i+1;
  7. }
  8. }
  9. }

赋值后数组元素的值分别是1,2,3,4,5。

4.1.5数组元素的访问

数组元素的访问需要根据数组名和下标共同实现,格式如下图4.5所示。 数组名[元素下标] = 元素值;

04.数组 - 图5

图4.5数组的赋值

例4.2 数组元素的访问。

  1. public class Array_sample2 {
  2. public static void main(String args[]){
  3. int num1[] = new int[5];
  4. num1[0] = 32; //每个元素单独初始化
  5. num1[1] = 543;
  6. num1[2] = 17;
  7. num1[3] = 8;
  8. num1[4] = 95;
  9. int num2[] = new int[10];
  10. for(int i=0;i<10;i++){ //采用循环方式初始化
  11. num2[i] = i+1;
  12. }
  13. System.out.println("第一个数组:");
  14. for(int j=0;j<num1.length;j++){ //采用循环方式输出数组的所有元素
  15. System.out.print(num1[j] +" ");
  16. }
  17. System.out.println();
  18. System.out.println("第二个数组:");
  19. for(int x=0;x<num2.length;x++){
  20. System.out.print(num2[x] +" ");
  21. }
  22. }
  23. }

运行结果如下图4.6所示。
04.数组 - 图6

图4.6数组的赋值

4.1.6数组的属性

数组是引用类型,具有属性,常用的数组属性就是数组的长度length,在使用时需注意:
–数组的长度(length)必须>=0;
–length为只读。
–利用length遍历数组

例4.3 求数组最大值、最小值和平均值。

  1. class ArrayMax {
  2. public static void main(String args[]){
  3. int a[] = {1,-12,33};
  4. int sum = a[0];
  5. int max = a[0];
  6. int min = a[0];
  7. for(int i=1;i<a.length;i++){
  8. sum = sum+a[i];
  9. if(a[i]>max){
  10. max = a[i];
  11. }
  12. if(a[i]<min){
  13. min = a[i];
  14. }
  15. }
  16. double ave = (double)sum/3;
  17. System.out.println("平均值"+ave);
  18. System.out.println("最大值"+max);
  19. System.out.println("最小值"+min);
  20. }
  21. }

运行结果如下图4.7所示。
04.数组 - 图7

图4.7数组的赋值

例4.4 数组应用例题。
假设队列中共有500人,每次从1开始数,数到3的人出队,下一个人接着从1开始数,编写程序找到最后剩下的人是哪一个。

  1. public class Count3Quit {
  2. public static void main(String args[]) {
  3. // 用布尔值来标识每个人是否出队 出队为false,未出队为true 并将每个人的初始值都赋为true
  4. boolean a[] = new boolean[500];
  5. for (int i = 0; i < a.length; i++) {
  6. a[i] = true;
  7. }
  8. /*leftNum:未出队的人数 countNum:取值为1、2、3, 每次从1开始数,数到3的人出队,下一个人接着从1开始数
  9. index:数数的人的编号,取值范围0~500
  10. */
  11. int leftNum = a.length;
  12. int countNum = 0;
  13. int index = 0;
  14. // 循环数数,直到只剩下一个人,即leftNum的值为1
  15. while (leftNum > 1) {
  16. if (a[index] == true) {
  17. countNum++;
  18. if (countNum == 3) {
  19. countNum = 0;
  20. a[index] = false;
  21. leftNum--;
  22. }
  23. }
  24. index++;
  25. // 如果数到第500个人,index回0,又从第1个人开始数
  26. if (index == a.length) {
  27. index = 0;
  28. }
  29. }
  30. //循环遍历每个数数的人,找到值为true的并输出
  31. for (int i = 0; i < a.length; i++) {
  32. if (a[i] == true)
  33. System.out.println("最后剩下的人是第" + (i + 1) + "的人");
  34. }
  35. }
  36. }

运行结果如下图4.8所示。
04.数组 - 图8

图4.8 Count3Quit运行结果

4.1.7数组的异常

应用数组时会出现两种异常,一种是空指针异常,类名为java.lang.NullPointerException,没有创建而直接使用数组时会产生该异常,另外一种是数组下标越界异常,类名是 java.lang.ArrayIndexOutOfBoundsException,访问数组时的下标超出数组的最大下标时产生该异常。

例4.5 数组空指针异常。

  1. public class Array_exception1 {
  2. public static void main(String args[]){
  3. int a[] = null ;
  4. a [0] = 1 ;
  5. System.out.println(a[0]);
  6. }
  7. }

运行结果如下图4.9所示。 04.数组 - 图9

图4.9数组的空指针异常

上题中,定义数组a之后,没有创建数组,而是直接给数组中的第一个元素a[0]进行了赋值,所以导致出现空指针异常。

例4.6 数组下标越界异常。

  1. class Array_exception2 {
  2. public static void main(String args[]) {
  3. // int a[]={1,2,3};
  4. int a[] = new int[3];
  5. a[0] = 1;
  6. a[1] = 2;
  7. a[2] = 3;
  8. a[3] = 4;
  9. System.out.println(a[3]);
  10. }
  11. }

运行结果如下图4.10所示。 04.数组 - 图10

图4.10数组的异常 上例子数组中共计3个元素,最大下标应该是2,因此当访问a[3]时,出现异常。

4.2 数组的排序和查找

4.2.1 数组的排序

冒泡排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。
冒泡排序流程至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。
image.png
image (1).png

例4.7 冒泡排序。

  1. public class Array_BubbleSort {
  2. public static void main(String[] args) {
  3. int[] arr = {9,8,5,4,2,0};
  4. /*
  5. * 趟i: 1,2,3,... arr.length-1
  6. *
  7. * 每趟次数 j: i=1 j=5(0,1,2,3,4)
  8. * i=2 j=4(0,1,2,3)
  9. * i=3 j=3
  10. *
  11. * j=arr.length-i-1
  12. *
  13. * */
  14. for(int i=1;i<arr.length;i++)//趟
  15. {
  16. for(int j=0;j<arr.length-i;j++)//次
  17. {
  18. if(arr[j]>arr[j+1])
  19. {
  20. //交换位置
  21. int tmp = arr[j];
  22. arr[j]=arr[j+1];
  23. arr[j+1]=tmp;
  24. }
  25. }
  26. }
  27. //打印数组
  28. for(int i=0; i<arr.length;i++)
  29. {
  30. System.out.println(arr[i]);
  31. }
  32. }
  33. }

4.2.1 数组的查找

二分查找的前提是需要查找的数组必须是已排序的,这里实现默认前提为升序。查找时将数组分为三部分,依次是中值(所谓的中值就是数组中间位置的那个值)前,中值,中值后;将要查找的值和数组中的值进行比较,若小于中值则在中值前面找,若大于中值则在中值后面找,等于中值时直接返回。从描述上就可以看出这个算法适合用循环来实现。
例4.8 数组二分查找。

  1. public class BinarySearch {
  2. public static void main(String[] args) {
  3. int[] sort = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  4. int key = 6;// 需要查找的值
  5. int locale = -1;// 记录 查找位置的变量
  6. int low = 0;
  7. int high = sort.length - 1;
  8. while (high >= low) {
  9. int mid = (low + high) / 2;
  10. if (key < sort[mid])
  11. high = mid - 1;
  12. else if (key == sort[mid]) {
  13. locale = mid;
  14. break;
  15. } else
  16. low = mid + 1;
  17. }
  18. if (locale == -1)
  19. System.out.println("数组中不存在元素" + key);
  20. else
  21. System.out.println("元素" + key + "在数组中的下标是" + locale);
  22. }
  23. }

程序运行结果如下图4.12所示。
04.数组 - 图13
图4.12二分查找法的程序运行结果

4.3数组的操作

4.3.1数组的复制

Java中的数组的复制使用System.arraycopy方法就可以实现,System.arraycopy所带参数如下所示: System.arraycopy(source,srcPos,dest,destPos,length)
复制source数组中从下标srcPos开始的length个元素到目标数组dest,并从目标数组的下标为destPos的位置开始储存 ,其中各个参数的含义如下:
@source: 源数组 srcPos: 源数组中的起始位置
@dest: 目标数组
@destPos:目标数组中的起始位置
@length: 要复制的数组元素的个数

例4.9 数组的复制。
复制数组{1,2,3,4,5}中从下标0开始的5个元素到目标数组{6,7,8,9,10,11,12,13,14,15},并从目标数组的下标为0的位置开始储存

  1. class Array_copy {
  2. public static void main(String args[]) {
  3. int aa[] = new int[] { 1, 2, 3, 4, 5 };
  4. int bb[] = new int[] { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
  5. System.arraycopy(aa, 0, bb, 0, 5);
  6. for (int i = 0; i < aa.length; i++) {
  7. System.out.print(aa[i] + " ");
  8. }
  9. System.out.println();
  10. for (int j = 0; j < bb.length; j++) {
  11. System.out.print(bb[j] + " ");
  12. }
  13. System.out.println();
  14. }
  15. }

运行结果如下图4.20所示。
04.数组 - 图14

图4.20 Array_copy的运行结果

  1. Arrays.sort(数组名)为数组排序的操作,但这个方法在 java.util 这个包里面,所以在用到的时候需要先将它导入。
  2. Arrays.sort方法有两种常用方式,具体参数如下:
  3. Arrays.sort(arr_name)
  4. Arrays.sort(arr_name,fromIndex,toIndex)

其中:

  1. arr_name:数组名称
  2. fromIndex:排序的起始下标
  3. toIndex:排序的终止下标


4.3.2数组的复制

例4.10 数组的排序。

  1. class ArraySort_sample {
  2. public static void main(String args[]){
  3. int[ ] point = {1,6,2,3,9,4,5,7,8};
  4. Arrays.sort(point);
  5. for(int i=0;i<point.length;i++){
  6. System.out.print(point[i]+" ");
  7. }
  8. }
  9. }

运行结果如下图4.21所示。
04.数组 - 图15
图**4.21 ArraySort_sample的运行结果
【总结与提示】**

  1. 1. 数组里只能存储相同类型的数据;
  2. 2. 数组的长度一旦创建便不能改变;
  3. 3. 声明数组时,方括号“[]”既可以放在数据类型的后面,也可以放在数组名的后面;
  4. 4. 数组的下标从0开始。

4.4.多维数组

前面讲的是一维数组。如果一维数组中的每一个元素也是一个数组,那么就是二维数组。以此类推,还可以有多维数组。
例4.11 创建以下二维数组。
Snipaste_2021-08-04_13-10-21.png

  1. int[][] num = new int[3][4];
  2. //给二维数组赋值
  3. for(int i=0;i<num.length;i++){
  4. for(int j=0;j<num[i].length;j++){
  5. num[i][j] = i+j;
  6. }
  7. }
  8. //遍历二维数组
  9. for(int i=0;i<num.length;i++){
  10. for(int j=0;j<num[i].length;j++){
  11. System.out.print(num[i][j] + " ");
  12. }
  13. System.out.println();
  14. }

例4.12 查找二维整型数组中的最大数及其位置。

  1. public class MulArrayMax2 {
  2. public static void main(String args[]) {
  3. int array[][] = { { 23, 2, 64, 16 }, { 35, 56, 97, 28 },{ 29, 10, 81, 12 } };
  4. int max = 0;
  5. int x = 0;
  6. int y = 0;
  7. for (int i = 0; i < array.length; i++) {
  8. for (int j = 0; j < array[i].length; j++) {
  9. if (array[i][j] > max) {
  10. max = array[i][j];
  11. x = i + 1;
  12. y = j + 1;
  13. }
  14. }
  15. }
  16. System.out.println("该二维数组中最大值是:" + max);
  17. System.out.println("位置是第" + x + "行,第" + y + "列");
  18. }
  19. }

运行结果如下图4.19所示。
04.数组 - 图17

图4.19 MulArrayMax2的运行结果

例4.13 求最高分和学号。

  1. public static void main(String[] args) {
  2. // 求最高分及对应的学号
  3. int[][] scores= {{10,88},{20,98},{30,89},{40,66},{50,77}};
  4. int max = scores[0][1];
  5. int no = scores[0][0];
  6. for(int i=1; i<scores.length;i++)
  7. {
  8. if(scores[i][1]>max)
  9. {
  10. max=scores[i][1];
  11. no=scores[i][0];
  12. }
  13. }
  14. System.out.println(max);
  15. System.out.println(no);
  16. }

4.5.课后作业

(一) 选择题
1. 创建二维数组a[3][4]后,a[0].length的值应该等于
A.0
B.2
C.3
D.4
2. 下列对长度为4的数组a的定义中,正确的是
A.int[4] a=new int[];
B.int a[]=new int[5];
C.int a[]={2,4,2,1};
D.int[4] a=new int[4];
3. 创建二维数组a[3][4]后,a.length的值应该等于
A.0
B.2
C.3
D.4
4. 下列关于数组的使用,不正确的是
A.数组长度定义后不可以修改;
B.数组中只能存放同类型数据;
C.数组下标范围从0开始最大到数组长度;
D.数组中可以存放普通数据也可以存对象;
(二)简答题
1.写出程序的运行结果。

  1. public class ClassX {
  2. public static void main(String[] args) {
  3. int a[]={45,18,98,56,304};
  4. for(int i=a.length-1;i>=0;i--)
  5. System.out.println(a[i]);
  6. }
  7. }

2.写出程序的运行结果。

  1. public class ABC {
  2. public static void main(String[] args) {
  3. int i,j;
  4. int a[]={9,27,10,1,49};
  5. for(i=0;i<a.length-1;i++){
  6. int k=i;
  7. for(j=i;j<a.length;j++)
  8. if(a[j]>a[k]) k=j;
  9. int temp=a[i];
  10. a[i]=a[k];
  11. a[k]=temp;
  12. }
  13. for(i=0;i<a.length;i++)
  14. System.out.println(a[i]+"");
  15. System.out.println();
  16. }
  17. }

3.阅读下列程序,写出程序的运行结果。

  1. public class abc{
  2. public static void main(String args[ ]){
  3. int i , s = 0 ;
  4. int a[ ] = { 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 };
  5. for ( i = 0 ; i < a.length ; i ++ ){
  6. if ( a[i]%3 == 0 )
  7. s += a[i] ;
  8. }
  9. System.out.println("s="+s);
  10. }
  11. }

(三)编程题
1、编写一个简单程序,要求数组长度为5,分别赋值10,20,30,40,50,在控制台输出该数组的值。

2、将一个字符数组的值(neusofteducation)拷贝到另一个字符数组中。

3、给定一个有9个整数(1,6,2,3,9,4,5,7,8)的数组,先排序,然后输出排序后的数组的值。

4、输出一个double型二维数组(长度分别为5、4,值自己设定)的值。

5、在一个有8个整数(18,25,7,36,13,2,89,63)的数组中找出其中最大的数及其下标。

6、将一个数组中的元素逆序存放。

7、将一个数组中的重复元素保留一个其他的清零。

8、给定一维数组{ -10,2,3,246,-100,0,5} ,计算出数组中的平均值、最大值、最小值。