在开发之中一定会使用到数组,但是数组使用的几率不是很高,而且也没这么复杂。
数组指的是一组相关变量的集合,例如,如果按照已有的概念,定义出100个int型变量,传统的做法就是“int i1,i2,..i100”,这个时候这些变量完全是独立的,没有任何的规律可寻,所以在操作之中就非常的麻烦,那么在这样的情况下就可以利用数组来解决问题,并且使用统一的索引进行数组的控制。
在Java之中,数组的定义格式如下:
声明并开辟数组: |
数据类型 数组名称 [] = new 数据类型 [长度] ; |
|
数据类型 [] 数组名称 = new 数据类型 [长度] ; |
||
分步进行: |
声明数组: |
数据类型 数组名称 [] = null ; |
数据类型 [] 数组名称 = null ; |
||
为数组开辟空间: |
数组名称 = new 数据类型 [长度] ; |
数组开辟之后,那么如果要进行访问可以采用“数组名称[下标]”的形式操作,但是下标的取值是从0开始的,其最大的长度是“数组长度 - 1”,例如:如果现在是“int data [] = new int [3] ;”,则下标的范围就是0 ~ 2(data[0]、data[1]、data[2]),并且所有的数组开辟之后,每一个元素的内容都是其对应数据类型的默认值。
但是数组作为一种顺序的存储结构,用户是可以利用循环来控制数组下标的操作,但是由于数组的长度都是固定的,所以如果要循环输出,则表示需要是一个已知长度的循环,那么一定使用的是for循环,可是数组的长度可以利用“数组名称.length”的形式取得。
范例:定义并使用数组
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [3] ; // 表示开辟3个元素的数组 System.out.println("数组长度:" + data.length) ; data[0] = 10 ; data[1] = 20 ; data[2] = 30 ; for (int x = 0 ; x < data.length ; x ++) { System.out.println(data[x]) ; } } } |
以上就是数组最基本的使用形式,而在本程序之中采用的是“声明并开辟数组”的格式定义的,那么下面也可以分步完成。
public class ArrayDemo { public static void main(String args[]) { int data [] = null ; // 声明数组,栈中声明 data = new int [3] ; // 表示开辟3个元素的数组 System.out.println("数组长度:" + data.length) ; data[0] = 10 ; data[1] = 20 ; data[2] = 30 ; for (int x = 0 ; x < data.length ; x ++) { System.out.println(data[x]) ; } } } |
通过两个简单的程序就可以发现数组的使用流程和对象的流程很相似,都存在有关键字new,而且一定也存在堆栈的引用关系,则现在可以画出如下的内存分配图。
在对象的堆内存里面保存的是属性,而在数组的堆内存之中保存的是数据,而且每一个数据要有对应的索引。那么既然此时发现数组的操作和对象很相似,而且数组本身也属于引用数据类型,则一定也可以发生引用传递。
范例:数组的引用传递
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [3] ; // 表示开辟3个元素的数组 data[0] = 10 ; data[1] = 20 ; data[2] = 30 ; // 直接写“data”表示地址,而“data[下标]”表示访问元素 int temp [] = data ; temp [0] = 100 ; // 修改第一个元素 for (int x = 0 ; x < data.length ; x ++) { System.out.println(data[x]) ; } } } |
与对象的引用传递没有任何的区别。
在之前所使用到的数组声明格式属于数组的动态初始化方式,动态初始化的特点就是开辟的数据长度由用户来决定,开辟之后的数据内容由系统来决定,系统认定的是对应数据类型的默认值。可如果现在用户希望数组开辟之后就可以是用户指定的数据,则可以采用静态初始化的方式完成。语法如下:
简便语法: |
数据类型 数组名称 [] = {值,值,值,...} ; |
完整语法: |
数据类型 数组名称 [] = new 数据类型 [] {值,值,值,...} ; |
为了更方便的使用数组,以后都使用完整语法编写。而使用静态数组初始化之后,数组的长度就是数据的个数,那么此时一定只能够使用“数组名称.length”取得长度。
范例:数组的静态初始化
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [] {5,6,1,2,7,9} ; for (int x = 0 ; x < data.length ; x ++) { System.out.println(data[x]) ; } } } |
此时数组中的内容在定义的时候就已经设置完成,所以现在的数组就不再需要由用户在进行数据的分配了。
数组的最基础概念就这么多,但是在数组上经常会出现许多的习题,例如:最著名的就是数组排序。按照升序的顺序排列数组的内容。
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [] {5,6,1,2,7,3} ; for (int x = 0 ; x < data.length ; x ++) { // 循环次数 for (int y = 0 ; y < data.length - 1 ; y ++) { if (data[y] > data[y + 1]) { // 交换数据 int temp = data[y] ; data[y] = data[y + 1] ; data[y + 1] = temp ; } } } for (int x = 0 ; x < data.length ; x ++) { System.out.print(data[x] + "、") ; } } } |
|
不引入第三个变量实现数据交换 |
public class Demo { public static void main(String args[]) { int x = 10 ; int y = 20 ; x = x + y ; y = x - y ; x = x - y ; System.out.println("x = " + x) ; System.out.println("y = " + y) ; } } |
这样的题目从实际来讲,只会在笔试之中出现,但是在应用层的开发之中,出现挺少的。
在前面所使用的数组严格来讲都是一维数组(一维数组只有一个“[]”),而二维数组就是有两个“[]”,如果说一维数组是一行数据,那么二维数组就是一个行、列的集合。下面做一个简单的比较:
· 一维数组:如果要想定位67这个数据只需要将索引设置为“2”即可。
索引 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
数据 |
89 |
80932 |
67 |
121 |
90 |
4223 |
10 |
20 |
30 |
· 二维数组:如果要确定888这个数据,需要行索引为2,列索引为5,才可以进行定位。
索引 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
0 |
89 |
80932 |
67 |
121 |
90 |
4223 |
10 |
20 |
30 |
1 |
9 |
0 |
09 |
90 |
9 |
9 |
9 |
9 |
72 |
2 |
2 |
2 |
4 |
9 |
0 |
888 |
9 |
78 |
8 |
3 |
97 |
9 |
69 |
6 |
96 |
5 |
67 |
8 |
0 |
如果要定义二维数组有两种形式:
动态初始化: |
数据类型 数组名称 [][] = new 数据类型[行数][列数]; |
静态初始化: |
数据类型 数组名称 [][] = new 数据类型[][] { {值,值,值,...} , {值,值,值,...} , ...} ; |
所以通过此代码就可以发现,二维数组就相当于数组之中又定义了其它的多个数组。
范例:定义二维数组
public class ArrayDemo { public static void main(String args[]) { int data [][] = new int [][] { {1,2} , {5,6,7,8} , {9,10,20}} ; for (int x = 0 ; x < data.length ; x ++) { // 控制行索引 for (int y = 0 ; y System.out.print(data[x][y] + "、") ; } System.out.println() ; } } } |
随着时间的推移,在Java的开发里面,二维数组出现的也少了,主要还是以一维数组为准。
数组的基本概念清楚之后,那么现在就还有一个问题出现了,既然数组是一种数据类型,那么在方法上就有可能接收或返回数组类型的数据。那么下面将通过一系列的程序来进行此类应用的分析。
范例:让方法的参数接收数组
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [] {1,3,5,7,9} ; printArray(data) ; // int temp [] = data ; } // 参数上明确表示要接收的数据是一个数组 public static void printArray(int temp[]) { for (int x = 0 ; x < temp.length ; x ++) { System.out.print(temp[x] + "、") ; } } } |
范例:现在假设有一个int型数据,在里面有多个数据,如果要想判断某一个数据是否存在,该如何操作?
· 幼儿园实现:
public class ArrayDemo { public static void main(String args[]) { int searchData = 3 ; // 待查找数据 int data [] = new int [] {1,3,5,7,9} ; boolean flag = false ; // 查询标记,默认为false for (int x = 0 ; x < data.length ; x ++) { if (data[x] == searchData) { // 查找 flag = true ; // 修改查询标记内容 break ; // 退出循环 } } System.out.println(flag ? "查找到了!" : "没有查找到!") ; } } |
此时的程序只能够说是完成了功能,但是这个代码并不好。实际上主方法是一切操作的起点,说白了,所有的客户操作都是在主方法上进行的。所以来讲在以后所有的开发之中,一定要本着一个原则:“主方法之中的代码越少越好,最好都简单的像 1 + 1应用那样”。所以这个时候就可以利用方法来简化代码。主方法客户端只需要调用方法就可以完成指定的功能。
· 利用方法减少客户端代码,在Java之中有一个命名规范,如果说某一个方法返回的数据类型是boolean,一般此方法名称都会以“isXxx()”的形式命名。
public class ArrayDemo { public static void main(String args[]) { int searchData = 3 ; // 待查找数据 int data [] = new int [] {1,3,5,7,9} ; System.out.println(isExists(searchData,data) ? "查找到了!" : "没有查找到!") ; } // 接收要查询的数据和数组内容 public static boolean isExists(int tempSearch,int arr[]) { for (int x = 0 ; x < arr.length ; x ++) { if (arr[x] == tempSearch) { return true ; // 直接结束 } } return false ; } } |
以上的两个程序只是简单的演示了两个方法接收数组直接操作的情况,实际上引用数据类型之中最大的特点还在于,方法里面可以修改数组内容。
范例:定义方法修改数组内容
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [] {1,3,5,7,9} ; System.out.print("原始数组内容:") ; printArray(data) ; incrementDouble(data) ; System.out.print("修改后的数组内容:") ; printArray(data) ; } public static void incrementDouble(int arr[]) { for (int x = 0 ; x < arr.length ; x ++) { arr[x] *= 2 ; // 扩大2倍 } } public static void printArray(int temp[]) { for (int x = 0 ; x < temp.length ; x ++) { System.out.print(temp[x] + "、") ; } System.out.println() ; } } |
引用传递的时候传递的是内存地址的使用权,所以在方法之中可以随意的进行原始数组内容的修改,下面通过内存关系图进行说明。
在引用数据类型操作之中,经常会出现在方法里面修改引用数据类型的形式,此时不要看代码,要回顾内存关系。
方法除了可以接收数组之外,也可以返回数组内容。
范例:定义方法返回数组
public class ArrayDemo { public static void main(String args[]) { int data [] = init() ; printArray(data) ; } public static int[] init() { return new int [] {1,3,5,7,9} ; // 匿名数组 } public static void printArray(int temp[]) { for (int x = 0 ; x < temp.length ; x ++) { System.out.print(temp[x] + "、") ; } System.out.println() ; } } |
范例:现在假设给出一个double型数组,要求定义一个方法,此方法可以统计出数组各个元素的和、平均值、最大值、最小值。
一般一个方法只能够返回一个数据类型,但是现在发现要返回的数据有四个,那么只能够将方法的返回值类型定义为数组,此数组第一个元素表示和,第二个元素表示平均值,第三个元素表示最大值,第四个元素表示最小值。
public class ArrayDemo { public static void main(String args[]) { double data [] = new double [] {10.2,5.6,1.3,8.0,7.9} ; double result [] = stat(data) ; // 接收统计结果 System.out.println("和:" + result[0]) ; System.out.println("平均:" + result[1]) ; System.out.println("最大:" + result[2]) ; System.out.println("最小:" + result[3]) ; } public static double[] stat(double temp[]) { double sum = temp[0] ; // 保存元素之和 double max = temp[0] ; // 第一个元素为最大值 double min = temp[0] ; // 第一个元素为最小值 for(int x = 1 ; x < temp.length ; x ++) { sum += temp[x] ; if (max < temp[x]) { max = temp[x] ; } if (min > temp[x]) { min = temp[x] ; } } double avg = sum / temp.length ; // 计算平均值 return new double [] {sum,avg,max,min} ; } public static void printArray(double temp[]) { for (int x = 0 ; x < temp.length ; x ++) { System.out.print(temp[x] + "、") ; } System.out.println() ; } } |
面试题:请将一个数组进行转置
情况一:转置的数组是一维数组;
原始数组: 1、2、3、4、5、6、7、8、9;
转置数组: 9、8、7、6、5、4、3、2、1。
对于此功能有两种实现方式:
· 方式一:定义一个新数组,而后将原始数组倒着保存到新数组之后,再去改变原始数组引用;
使用此种模式一定会产生垃圾空间,而产生垃圾空间的代码我们应该尽可能进行回避。
· 方式二:实现数据的首尾交换,此时要分两种数据情况进行分析:一个是数组的长度为奇数长度,另一个是数组长度为偶数长度;
范例:实现数组的转置
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [] {0,1,2,3,4,5,6,7,8,9} ; reverse(data) ; printArray(data) ; } public static void reverse(int arr[]) { // 数组转置 int center = arr.length / 2 ; // 计算中间点,循环次数 int head = 0 ; // 左边下标控制 int tail = arr.length - 1 ; // 右边下标控制 for (int x = 0 ; x < center ; x ++) { int temp = arr[head] ; arr[head] = arr[tail] ; arr[tail] = temp ; head ++ ; tail -- ; } } public static void printArray(int temp[]) { for (int x = 0 ; x < temp.length ; x ++) { System.out.print(temp[x] + "、") ; } System.out.println() ; } } |
情况二:二维数组转置
如果要想实现二维数组的转置必须有一个前提:行和列的长度相同。
public class ArrayDemo { public static void main(String args[]) { int data [][] = new int [][] { {0,1,2},{3,4,5},{6,7,8}} ; reverse(data) ; printArray(data) ; } public static void reverse(int arr[][]) { // 数组转置 for(int x = 0 ; x < arr.length ; x ++) { for (int y = 0 ; y < x ; y ++) { if (x != y) { // 可以转换 int temp = arr[x][y] ; arr[x][y] = arr[y][x] ; arr[y][x] = temp ; } } } } public static void printArray(int temp[][]) { for (int x = 0 ; x < temp.length ; x ++) { for (int y = 0 ; y < temp[x].length ; y ++) { System.out.print(temp[x][y] + "、") ; } System.out.println() ; } System.out.println() ; } } |
对于此类的转换题目而言,有一部分是逻辑的思维控制能力,属于专业的基本功问题。对于数组至少要求掌握:数组排序和数组转置。
Java平台对于数组提供了很好的技术支持,今天首先来看两个支持的方法。
1、 数组拷贝:
· 语法(不完整):System.arraycopy(源数组名称,源数组开始点,目标数组名称,目标数组开始点,拷贝长度);
范例:实现数组拷贝
· 数组一:1、2、3、4、5、6、7、8、9;
· 数组二:11、22、33、44、55、66、77、88、99;
· 拷贝后的数组一:1、2、3、55、66、77、7、8、9。
public class ArrayDemo { public static void main(String args[]) { int dataA [] = new int [] {1,2,3,4,5,6,7,8,9} ; int dataB [] = new int [] {11,22,33,44,55,66,77,88,99} ; System.arraycopy(dataB,4,dataA,3,3) ; printArray(dataA) ; } public static void printArray(int temp[]) { for (int x = 0 ; x < temp.length ; x ++) { System.out.print(temp[x] + "、") ; } System.out.println() ; }} |
思考题:现在原本存在了一个新的数组,假设数组的内容为{1,3,5,7},要求可以扩充数组的容量,例如:如果扩充的容量为2,则在已有长度基础上增加2。
在本程序之中重点实际有两处:
· 扩充容量,数组的长度永恒都是固定的,所以此时只能够开辟一个新的数组,新数组有新的长度;
· 原始数据保留,应该在新数组之中保存已有的数据,则表示将旧的数据拷贝到新数组之中。
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [] {1,3,5,7} ; data = increment(data,5) ; // 改变引用关系 printArray(data) ; } public static int [] increment(int temp[],int len) { int newArr [] = new int [temp.length + len] ; // 开辟新数组 System.arraycopy(temp,0,newArr,0,temp.length) ; return newArr ; // 返回新数组 } public static void printArray(int temp[]) { for (int x = 0 ; x < temp.length ; x ++) { System.out.print(temp[x] + "、") ; } System.out.println() ; } } |
2、 数组排序:
· 语法:java.util.Arrays.sort(数组名称);
public class ArrayDemo { public static void main(String args[]) { int data [] = new int [] {8,6,1,0,10,20,3} ; java.util.Arrays.sort(data) ; printArray(data) ; } public static void printArray(int temp[]) { for (int x = 0 ; x < temp.length ; x ++) { System.out.print(temp[x] + "、") ; } System.out.println() ; }} |
如果日后你在笔试之中出现了一个数组排序的题目,请千万别直接写上“java.util.Arrays.sort()”排序,您一定要写上完整的代码,可以在最后补充一句。
在之前所使用的数组严格来讲都算是基本数据类型的数组应用。但是在开发之中,对象数组也是一个非常重要的概念,而顾名思义,对象数组包含的一定是多个操作对象。而对象数组的定义语法如下:
动态初始化: |
类名称 对象数组名称 [] = new 类名称 [长度]; |
静态初始化: |
类名称 对象数组名称 [] = new 类名称 [] {实例化对象, 实例化对象,…}; |
如果使用的是动态初始化,则对象数组之中每一个元素的内容都将是null。
范例:对象数组动态初始化
class Person { private String name ; private int age ; public Person(String n,int a) { name = n ; age = a ; } // setter、getter略 public String getInfo() { return "姓名:" + name + ",年龄:" + age ; } } public class ArrayDemo { public static void main(String args[]) { Person per [] = new Person [3] ; // 对象数组 per[0] = new Person("张三",20) ; per[1] = new Person("李四",21) ; per[2] = new Person("王五",22) ; for (int x = 0 ; x < per.length ; x ++) { System.out.println(per[x].getInfo()) ; } } } |
范例:对象数组的静态初始化
class Person { private String name ; private int age ; public Person(String n,int a) { name = n ; age = a ; } // setter、getter略 public String getInfo() { return "姓名:" + name + ",年龄:" + age ; } } public class ArrayDemo { public static void main(String args[]) { Person per [] = new Person [] { new Person("张三",20), new Person("李四",21) , new Person("王五",22)} ; // 对象数组 for (int x = 0 ; x < per.length ; x ++) { System.out.println(per[x].getInfo()) ; } } } |
public class ArrayDemo { public static void main(String args[]) { Person perA = new Person("张三",20) ; Person perB = new Person("李四",21) ; Person perC = new Person("王五",22) ; Person per [] = new Person [] {perA,perB,perC} ; // 对象数组 for (int x = 0 ; x < per.length ; x ++) { System.out.println(per[x].getInfo()) ; } } } |
关于对象数组更多的内容,将在以后进行扩充讲解,今天就要求掌握对象数组的定义形式。