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

4.1一维数组

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

4.1.1数组的定义

  1. 我们用方括号“[]”来区分普通变量和数组变量。见下面声明整型数组的例子:
  1. int[] numbers;
  1. 上面的语句声明了一个数组numbers,它的元素都是整型的,但是现在并没有确定数组的长度,也就是说没有确定numbers里有多少个元素。

数组声明的语法是:

  1. 数据类型[] 数组名;
  2. 或者
  3. 数据类型 数组名[];

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

例如:

  1. char[] c;
  2. String strs[];
  1. 数组声明后,Java虚拟机就会给数组分配存储空间,情况如下图4.1所示。

04.数组 - 图1

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

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

4.1.2数组的创建

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

1.静态初始化

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

2.动态初始化

  1. 这种方式只是按照指定的长度来创建数组空间,但是数组里的元素将初始化为缺省值(对字符来说,为’\u0000’),例如:
  1. numbers = new int[6];
  1. 上面的语句运行后,会创建一个数组numbers,这个数组的长度是6,但是值都是0,使用这个数组前还需要进一步的赋值使之有效。

这种创建方式的语法是:

  1. 数组名 = new 数组类型[数组长度];

如:

  1. char[] c = new char[128];
  2. String[] strs = new String[10];
  3. double[] incoming = new double[73];

04.数组 - 图2

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

4.1.3数组的内存模型

  1. 数组是存储多个相同类型数据的对象。数组的所有元素保存在堆内存中。
  2. 创建一个数组就是在堆中创建一个数组对象。数组创建后立即拥有默认值。索引从0开始。连续分配

例如:

  1. int[ ] array ;
  2. array = new int [4] ;

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

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

4.1.4数组的初始化

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

数组的初始化的两种方式如下图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=1;i<5;i++){
  6. a[i-1] = i;
  7. }
  8. }
  9. }
  1. 赋值后数组元素的值分别是1,2,3,4,5

4.1.5数组元素的访问

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

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,在使用时需注意:

  1. –数组的长度(length)必须>=0
  2. length为只读。
  3. –利用length遍历数组

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

  1. class ArrayMax {
  2. public static void main(String args[]){
  3. int a[] = {1,-12,33};
  4. int sum = 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 数组应用例题。

  1. 假设队列中共有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. //循环遍历每个数数的人,找到值为false的并输出
  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数组的异常

  1. 应用数组时会出现两种异常,一种是空指针异常,类名为java.lang.NullPointerException,没有创建而直接使用数组时会产生该异常,另外一种是数组下标越界异常,类名是
  2. 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数组的空指针异常

  1. 上题中,定义数组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.1.数组的排序查找

  1. 排序是计算机程序设计中的常见工作,例如把无序的成绩列表按由大到小的顺序输出,排序的算法也较多,本节介绍较为经典的冒泡排序法。和排序一样,查找是指在数组中寻找特定元素的过程。例如,查找商品列表中某个价格的商品,查找订单列表中某一份订单等等,在本节中介绍一种常见的算法:二分查找法。排序和查找也经常结合起来应用。
  2. 冒泡排序法(Bubble Sort,也称之为气泡排序法)是一种简单的排序算法。它重复地走访过要排序的数组,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数组的工作是重复地进行直到没有再需要交换,也就是说该数组已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数组的顶端,故名冒泡排序。
  3. 冒泡排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。
  4. 冒泡排序流程至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。

下面我们通过一个例子来看一下冒泡排序。 例4.7 数组排序。

  1. public class Array_BubbleSort {
  2. public static void main(String[] args) {
  3. int[] sort = { 1, 6, 2, 3, 9, 4, 5, 7, 8 };
  4. System.out.print("排序前:");
  5. for (int i = 0; i < sort.length; i++) {
  6. System.out.print(sort[i] + " ");
  7. }
  8. System.out.println();
  9. for (int i = 0; i < sort.length; i++) {
  10. int temp = 0;
  11. for (int j = sort.length - 1; j > 0; j--) {
  12. if (sort[j] > sort[j - 1]) {
  13. temp = sort[j];
  14. sort[j] = sort[j - 1];
  15. sort[j - 1] = temp;
  16. }
  17. }
  18. }
  19. System.out.print("排序后:");
  20. for (int i = 0; i < sort.length; i++) {
  21. System.out.print(sort[i] + " ");
  22. }
  23. }
  24. }

程序运行结果如下图4.11所示。
04.数组 - 图11

图4.11冒泡排序法的程序运行结果

  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.数组 - 图12
图4.12二分查找法的程序运行结果
冒泡排序法也可以用Java自带的java.util.Arrays类中的sort方法实现,用法如下: 例4.9 数组二分查找。

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

4.3数组的操作

4.3.1数组的遍历

数组的遍历需要利用循环语句和数组下标,下面通过几个例子来学习数组的遍历。 例4.11 循环遍历字符串数组 {“red”,”orange”,”yellow”,”green”,”blue”,”purple”},并将其打印

  1. class Array_traversal {
  2. public static void main(String args[]){
  3. String color[] = {"red","orange","yellow","green","blue","purple"};
  4. for(int i=0;i<color.length;i++){
  5. System.out.println(color[i]);
  6. }
  7. }
  8. }

运行结果如下图4.17所示。
04.数组 - 图13

图4.17 Array_traversal的运行结果

例4.12 求最高分和学号。
04.数组 - 图14
运行结果如下图4.18所示。
04.数组 - 图15

图4.18 MulArrayMax1的运行结果

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

public class MulArrayMax2 {
         public static void main(String args[]) {
                   int array[][] = { { 23, 2, 64, 16 }, { 35, 56, 97, 28 },{ 29, 10, 81, 12 } };
                   int max = 0;
                   int x = -1;
                   int y = -1;
                   for (int i = 0; i < array.length; i++) {
                            for (int j = 0; j < array[i].length; j++) {
                                     if (array[i][j] > max) {
                                               max = array[i][j];
                                               x = i + 1;
                                               y = j + 1;
                                    }
                                     System.out.print(array[i][j] + "\t");
                            }
                           System.out.println();
                   }
                   System.out.println("该二维数组中最大值是:" + max);
                   System.out.println("位置是第" + x + "行,第" + y + "列");
         }
}

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

图4.19 MulArrayMax2的运行结果

4.3.2*数组的复制和排序**

Java中的数组的复制使用System.arraycopy方法就可以实现,System.arraycopy所带参数如下所示:
        System.arraycopy(source,srcPos,dest,destPos,length)

复制source数组中从下标srcPos开始的length个元素到目标数组dest,并从目标数组的下标为destPos的位置开始储存 ,其中各个参数的含义如下:

source: 源数组 
srcPos: 源数组中的起始位置 
dest: 目标数组 
destPos:目标数组中的起始位置 
length: 要复制的数组元素的个数

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

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

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

图4.20 Array_copy的运行结果

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

其中:

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

例4.15 数组的排序。

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

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

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

4.4课后作业

(一) 选择题

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. 写出程序的运行结果。

    public class ClassX {
    public static void main(String[] args) {
          int a[]={45,18,98,56,304};
          for(int i=a.length-1;i>=0;i--)
                System.out.println(a[i]);
    }
    }
    
  2. 写出程序的运行结果。

    public class ABC {
    public static void main(String[] args) {
           int i,j;
           int a[]={9,27,10,1,49};
           for(i=0;i<a.length-1;i++){
                  int k=i;
                  for(j=i;j<a.length;j++)
                         if(a[j]>a[k]) k=j;
                  int temp=a[i];
                  a[i]=a[k];
                  a[k]=temp;            
           }
          for(i=0;i<a.length;i++)
                 System.out.println(a[i]+"");
           System.out.println();
    }
    }
    
  3. (三)编程题

    1. 编写一个简单程序,要求数组长度为5,分别赋值10,20,30,40,50,在控制台输出该数组的值。
    2. 将一个字符数组的值(neusofteducation)拷贝到另一个字符数组中。
    3. 给定一个有9个整数(1,6,2,3,9,4,5,7,8)的数组,先排序,然后输出排序后的数组的值。
    4. 有2个多维数组分别如下所示

04.数组 - 图1904.数组 - 图20

按照如下方式进行运算。生成一个2行4列的数组。此数组的第1行1列是2*1+3*5+4*2第1行2列是2*5+3*9+4*7 第2行1列是4*1+6*5+8*2 依次类推。
5.       输出一个double型二维数组(长度分别为5、4,值自己设定)的值。
6.      在一个有8个整数(18,25,7,36,13,2,89,63)的数组中找出其中最大的数及其下标。 
7.       将一个数组中的元素逆序存放。
8.      将一个数组中的重复元素保留一个其他的清零。
9.      给定一维数组{ -10,2,3,246,-100,0,5} ,计算出数组中的平均值、最大值、最小值。