一.单元概述
在Java语言中,有一种特殊的引用类型叫数组,它在概念上属于类的范畴,但处理的方式又与类和对象不完全相同,本章节主要重点介绍数组的定义和创建以及常见的应用。
二、重点与难点
(1) 一维数组;
(2) 数组的异常;
(3) 数组的操作。
难点:
(1) 多维数组的内存模型;
(2) 数组的复制和排序。
4.1一维数组
数组是相同类型的数据按照顺序组合后的一种引用类型。数组可以看成是多个相同类型数据的组合,实现对这些数据的统一管理。数组的长度一旦确定后就不能更改,因此它是一个固定长度的结构。数组结构中每个存储的数据叫数组元素,数组元素由索引来定位。索引(或叫数组下标)是指当前元素相对于第一个元素的位移,因此第一个元素的下标就是0,第二个元素的下标就是1,以此类推。
4.1.1数组的定义
我们用方括号“[]”来区分普通变量和数组变量。见下面声明整型数组的例子:
int[] numbers;
上面的语句声明了一个数组numbers,它的元素都是整型的,但是现在并没有确定数组的长度,也就是说没有确定numbers里有多少个元素。
数组声明的语法是:
数据类型[] 数组名;
或者
数据类型 数组名[];
提示: 方括号“[]”既可以放在数据类型的后面,也可以放在数组名的后面,两者都可以标识一个数组的声明,但是第一种写法可读性更好些。
例如:
char[] c;
String strs[];
数组声明后,Java虚拟机就会给数组分配存储空间,情况如下图4.1所示。
图4.1数组声明后的内存情况示意图
数组在声明后,但是没有创建前,并没有给它分配具体的内存空间,所以这个时候访问数组会出现NullPointerException异常。我们需要在访问数组前创建数组,确定数组的长度,以便能为数组分配内存空间。
4.1.2数组的创建
数组的长度在创建时指定,并且一旦指定就不能更改。创建数组有两种方式:
1.静态初始化
这种方式在声明的同时就直接初始化数组,同时也创建了数组空间。如:
int[] m = {3,75,234,41,16};
上面的语句运行后,就会创建一个数组m,这个数组的长度是5,他有5个元素,分别是3,75,234,41,16。
2.动态初始化
这种方式只是按照指定的长度来创建数组空间,但是数组里的元素将初始化为缺省值(对字符来说,为’\u0000’),例如:
numbers = new int[6];
上面的语句运行后,会创建一个数组numbers,这个数组的长度是6,但是值都是0,使用这个数组前还需要进一步的赋值使之有效。
这种创建方式的语法是:
数组名 = new 数组类型[数组长度];
如:
char[] c = new char[128];
String[] strs = new String[10];
double[] incoming = new double[73];
图4.2数组创建后的内存情况示意图
4.1.3数组的内存模型
数组是存储多个相同类型数据的对象。数组的所有元素保存在堆内存中。
创建一个数组就是在堆中创建一个数组对象。数组创建后立即拥有默认值。索引从0开始。连续分配
例如:
int[ ] array ;
array = new int [4] ;
数组的内存模型如下图4.3所示。
图4.3数组的内存模型示意图
4.1.4数组的初始化
数组的元素通过下标来标识,下标表示当前元素相对于第一个元素的位移,因此从0开始。比如name[0]就表示name数组的第1个元素,name[4]就表示name数组的第5个元素。其中方括号“[]”中的整数就是下标(又叫索引),要注意下标的范围从0~数组长度-1,如果访问超过范围的下标,就会发生ArrayIndexOutOfBoundsException下标越界异常。
数组类型 数组名[ ] = {元素1,元素2,…}
或者
数组类型 数组名[ ] = new 数据类型[ ]{元素1,元素2,…}
数组的初始化的两种方式如下图4.4所示。
图4.4数组的初始化
例4.1 创建数组并初始化。
class Array_sample1 {
public static void main(String args[]){
int a[];
a = new int[5];
for(int i=1;i<5;i++){
a[i-1] = i;
}
}
}
赋值后数组元素的值分别是1,2,3,4,5。
4.1.5数组元素的访问
数组元素的访问需要根据数组名和下标共同实现,格式如下图4.5所示。
数组名[元素下标] = 元素值;
图4.5数组的赋值
例4.2 数组元素的访问。
public class Array_sample2 {
public static void main(String args[]){
int num1[] = new int[5];
num1[0] = 32; //每个元素单独初始化
num1[1] = 543;
num1[2] = 17;
num1[3] = 8;
num1[4] = 95;
int num2[] = new int[10];
for(int i=0;i<10;i++){ //采用循环方式初始化
num2[i] = i+1;
}
System.out.println("第一个数组:");
for(int j=0;j<num1.length;j++){ //采用循环方式输出数组的所有元素
System.out.print(num1[j] +" ");
}
System.out.println();
System.out.println("第二个数组:");
for(int x=0;x<num2.length;x++){
System.out.print(num2[x] +" ");
}
}
}
运行结果如下图4.6所示。
图4.6数组的赋值
4.1.6数组的属性
数组是引用类型,具有属性,常用的数组属性就是数组的长度length,在使用时需注意:
–数组的长度(length)必须>=0;
–length为只读。
–利用length遍历数组
例4.3 求数组最大值、最小值和平均值。
class ArrayMax {
public static void main(String args[]){
int a[] = {1,-12,33};
int sum = 0;
int max = a[0];
int min = a[0];
for(int i=1;i<a.length;i++){
sum = sum+a[i];
if(a[i]>max){
max = a[i];
}
if(a[i]<min){
min = a[i];
}
}
double ave = (double)sum/3;
System.out.println("平均值"+ave);
System.out.println("最大值"+max);
System.out.println("最小值"+min);
}
}
运行结果如下图4.7所示。
图4.7数组的赋值
例4.4 数组应用例题。
假设队列中共有500人,每次从1开始数,数到3的人出队,下一个人接着从1开始数,编写程序找到最后剩下的人是哪一个。
public class Count3Quit {
public static void main(String args[]) {
// 用布尔值来标识每个人是否出队 出队为false,未出队为true 并将每个人的初始值都赋为true
boolean a[] = new boolean[500];
for (int i = 0; i < a.length; i++) {
a[i] = true;
}
/*leftNum:未出队的人数 countNum:取值为1、2、3, 每次从1开始数,数到3的人出队,下一个人接着从1开始数
index:数数的人的编号,取值范围0~500
*/
int leftNum = a.length;
int countNum = 0;
int index = 0;
// 循环数数,直到只剩下一个人,即leftNum的值为1
while (leftNum > 1) {
if (a[index] == true) {
countNum++;
if (countNum == 3) {
countNum = 0;
a[index] = false;
leftNum--;
}
}
index++;
// 如果数到第500个人,index回0,又从第1个人开始数
if (index == a.length) {
index = 0;
}
}
//循环遍历每个数数的人,找到值为false的并输出
for (int i = 0; i < a.length; i++) {
if (a[i] == true)
System.out.println("最后剩下的人是第" + (i + 1) + "的人");
}
}
}
运行结果如下图4.8所示。
图4.8 Count3Quit运行结果
4.1.7数组的异常
应用数组时会出现两种异常,一种是空指针异常,类名为java.lang.NullPointerException,没有创建而直接使用数组时会产生该异常,另外一种是数组下标越界异常,类名是
java.lang.ArrayIndexOutOfBoundsException,访问数组时的下标超出数组的最大下标时产生该异常。
例4.5 数组空指针异常。
public class Array_exception1 {
public static void main(String args[]){
int a[] = null ;
a [0] = 1 ;
System.out.println(a[0]);
}
}
运行结果如下图4.9所示。
图4.9数组的空指针异常
上题中,定义数组a之后,没有创建数组,而是直接给数组中的第一个元素a[0]进行了赋值,所以导致出现空指针异常。
例4.6 数组下标越界异常。
class Array_exception2 {
public static void main(String args[]) {
// int a[]={1,2,3};
int a[] = new int[3];
a[0] = 1;
a[1] = 2;
a[2] = 3;
a[3] = 4;
System.out.println(a[3]);
}
}
运行结果如下图4.10所示。
图4.10数组的异常 上例子数组中共计3个元素,最大下标应该是2,因此当访问a[3]时,出现异常。
4.1.数组的排序查找
排序是计算机程序设计中的常见工作,例如把无序的成绩列表按由大到小的顺序输出,排序的算法也较多,本节介绍较为经典的冒泡排序法。和排序一样,查找是指在数组中寻找特定元素的过程。例如,查找商品列表中某个价格的商品,查找订单列表中某一份订单等等,在本节中介绍一种常见的算法:二分查找法。排序和查找也经常结合起来应用。
冒泡排序法(Bubble Sort,也称之为气泡排序法)是一种简单的排序算法。它重复地走访过要排序的数组,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数组的工作是重复地进行直到没有再需要交换,也就是说该数组已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数组的顶端,故名冒泡排序。
冒泡排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。
冒泡排序流程至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。
下面我们通过一个例子来看一下冒泡排序。 例4.7 数组排序。
public class Array_BubbleSort {
public static void main(String[] args) {
int[] sort = { 1, 6, 2, 3, 9, 4, 5, 7, 8 };
System.out.print("排序前:");
for (int i = 0; i < sort.length; i++) {
System.out.print(sort[i] + " ");
}
System.out.println();
for (int i = 0; i < sort.length; i++) {
int temp = 0;
for (int j = sort.length - 1; j > 0; j--) {
if (sort[j] > sort[j - 1]) {
temp = sort[j];
sort[j] = sort[j - 1];
sort[j - 1] = temp;
}
}
}
System.out.print("排序后:");
for (int i = 0; i < sort.length; i++) {
System.out.print(sort[i] + " ");
}
}
}
程序运行结果如下图4.11所示。
图4.11冒泡排序法的程序运行结果
二分查找的前提是需要查找的数组必须是已排序的,这里实现默认前提为升序。查找时将数组分为三部分,依次是中值(所谓的中值就是数组中间位置的那个值)前,中值,中值后;将要查找的值和数组中的值进行比较,若小于中值则在中值前面找,若大于中值则在中值后面找,等于中值时直接返回。从描述上就可以看出这个算法适合用循环来实现。
例4.8 数组二分查找。
public class BinarySearch {
public static void main(String[] args) {
int[] sort = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int key = 6;// 需要查找的值
int locale = -1;// 记录 查找位置的变量
int low = 0;
int high = sort.length - 1;
while (high >= low) {
int mid = (low + high) / 2;
if (key < sort[mid])
high = mid - 1;
else if (key == sort[mid]) {
locale = mid;
break;
} else
low = mid + 1;
}
if (locale == -1)
System.out.println("数组中不存在元素" + key);
else
System.out.println("元素" + key + "在数组中的下标是" + locale);
}
}
程序运行结果如下图4.12所示。
图4.12二分查找法的程序运行结果
冒泡排序法也可以用Java自带的java.util.Arrays类中的sort方法实现,用法如下: 例4.9 数组二分查找。
import java.util.Arrays;
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.3数组的操作
4.3.1数组的遍历
数组的遍历需要利用循环语句和数组下标,下面通过几个例子来学习数组的遍历。 例4.11 循环遍历字符串数组 {“red”,”orange”,”yellow”,”green”,”blue”,”purple”},并将其打印
class Array_traversal {
public static void main(String args[]){
String color[] = {"red","orange","yellow","green","blue","purple"};
for(int i=0;i<color.length;i++){
System.out.println(color[i]);
}
}
}
运行结果如下图4.17所示。
图4.17 Array_traversal的运行结果
例4.12 求最高分和学号。
运行结果如下图4.18所示。
图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所示。
图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所示。
图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所示。
图**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.数组中可以存放普通数据也可以存对象;
(二)简答题
写出程序的运行结果。
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]); } }
写出程序的运行结果。
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(); } }
(三)编程题
- 编写一个简单程序,要求数组长度为5,分别赋值10,20,30,40,50,在控制台输出该数组的值。
- 将一个字符数组的值(neusofteducation)拷贝到另一个字符数组中。
- 给定一个有9个整数(1,6,2,3,9,4,5,7,8)的数组,先排序,然后输出排序后的数组的值。
- 有2个多维数组分别如下所示
和
按照如下方式进行运算。生成一个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} ,计算出数组中的平均值、最大值、最小值。