1、课程名称:数组的定义及使用
2、知识点
2.1、上次课程的主要知识点
1、类与对象的组成以及加强;
2、简单Java类开发。
2.2、本次预计讲解的知识点
1、数组的基本定义;
2、数组的使用;
3、数组与方法间的互操作;
4、数组有关的操作类库支持。
3、具体内容(★★★★☆)
所有的开发之中都一定要使用到数组,但是数组没有讲解的这么复杂。之所以本次要讲解的比较多,主要是为了防止笔试中出现的问题。
3.1、数组的基本概念
数组指的是一组相关变量的集合。如果说现在要求你定义100个整型变量,那么按照最原始的方式则肯定这样定义:
这种操作可以实现要求,但是这些变量的关联实在是太麻烦了。为此在开发之中可以利用数组来解决这一问题。
在Java中数组属于引用数据类型,既然是引用数据类型就牵扯到内存的关系。对于数组的定义语法有以下两种形式:·声明并开辟数组:数据类型数组名称[] = new 数据类型[长度];
数据类型[] 数组名称= new 数据类型[长度];
·分步完成:
|- 声明数组:数据类型数组名称[] = null;数据类型[] 数组名称= null;
|- 开辟数组:数组名称= new 数据类型[长度];
当数组开辟空间之后那么就可以采用“数组[索引]”的形式进行数组的访问,但是需要注意的是,如果现在数组的长度为3,那么索引的范围:0 ~ 2(一共3个元素)。如果操作中超过了数组的允许索引范围,则程序在运行过程之中会出现“ArrayIndexOutOfBoundsException”(数组索引超出绑定异常,数组越界)。
以上的操作属于数组的动态初始化,动态初始化的特点,是数组开辟空间之后,数组中每个元素的内容都是其对应数据类型的默认值。
范例:定义数组
范例:数组的赋值操作
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data [] = new int [3] ; // 开辟了一个data数组,长度为3
data[0] = 10 ; // 为数组赋值
data[1] = 20 ;
data[2] = 30 ;
System.out.println(data[0]) ;
System.out.println(data[1]) ;
System.out.println(data[2]) ;
}
}
但是现在数组本身是一种顺序式的结构,所以在进行数组内容输出的时候,往往可以采用循环的方式完成。由于数组的长度是固定的,所以只要是进行数组的输出那么都会考虑使用for循环,这里面就牵扯到了一个数组长度的取得,在
Java中可以使用“数组对象.length”属性取得。
范例:数组输出
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data [] = new int [3] ; // 开辟了一个data数组,长度为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.print(data[x] + "、") ;
}
}
}
3.2、数组的引用分析
引用数据类型的分析过程几乎都是一样的,所以数组的引用分析其本质也是一样的,与对象的流程是相同的,以下面的代码为例。
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data [] = new int [3] ; // 开辟了一个data数组,长度为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.print(data[x] + "、") ;
}
}
}
以上的流程与普通对象本质上是没有什么区别的,唯一的区别是在于普通的类对象是保存属性,利用属性名称来操作,但是数组保存的是一组内容,利用索引来操作。
那么既然此关系可以弄明白,所谓的分步的操作也就一样的过程。
范例:分步实例化数组对象
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data [] = null ; // 只有声明的时候加上“[]”
data = new int [3] ; // 开辟了一个data数组,长度为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.print(data[x] + "、") ;
}
}
}
那么现在如果此关系可以明白的话,就意味着可以进行数组的引用操作了。引用的本质同一块堆内存空间被不同的栈内存所指向。
范例:数组的引用操作
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data [] = new int [3] ; // 开辟了一个data数组,长度为3
data[0] = 10 ; // 为数组赋值
data[1] = 20 ;
data[2] = 30 ;
int temp [] = data ; // 引用操作
temp [0] = 100 ; // 修改数据
for (int x = 0 ; x < data.length ; x ++) {
System.out.print(data[x] + "、") ;
}
}
}
还是通过内存关系图来进行操作描述。
3.3、数组的静态初始化
以上是针对于数组的动态初始化操作讲解的,动态初始化操作的特点在于其只能够在开辟数组空间之后进行数组内容的赋值,如果现在希望数组开辟之后就可以存在明确的内容,那么则可以使用数组的静态初始化,而语法有如下两种:第一种:简化型
数据类型数组名称[] = {值,值,...} ;
数据类型[] 数组名称= {值,值,...} ;
第二种:完全型(推荐使用)
数据类型数组名称[] = new 数据类型[] {值,值,...} ;
数据类型[] 数组名称= new 数据类型[] {值,值,...} ;
范例:使用数组的静态初始化
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data []= new int [] {10,20,30} ; // 数组的静态初始化
for (int x = 0 ; x < data.length ; x ++) {
System.out.print(data[x] + "、") ;
}
}
}
范例:判断某一个数字是否在数组中存在?
最简单的做法是进行for循环操作,一次与每一个数组中的内容进行相等比较。
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int sdata = 11 ; // 要查找的数据
boolean flag = false ; // 保存查找结果,如果找到了修改为true
int data []= new int [] {10,20,30,11,22,33} ;
for (int x = 0 ; x < data.length ; x ++) {
if (data [x] == sdata) { // 现在已经查找到了内容
flag = true ;
break ; // 退出循环
}
}
if (flag) {
System.out.println("该数据已经查找到!") ;
} else {
System.out.println("该数据没有查找到!") ;
}
}
}
但是严格来讲现在这样的代码性能并不好,因为它需要将数组中的每一个元素都进行判断。面试题:请解释什么叫二分查找法(折半查找法)
二分查找法的重要支持:数组中的数据必须是有序的。
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int sdata = 161 ;
int data [] = new int [] {10,20,30,40,50,60} ;
System.out.println(search(sdata,data)) ;
}
public static boolean search(int sdate,int [] data) {
System.out.print(java.util.Arrays.toString(data) + " len = ") ;
if (data.length == 1) {
return sdate == data[0] ;
} else {
int len = data.length / 2 ; // 012=1、01=1
System.out.println(len + "," + (sdate == data[len])) ;
if (sdate > data[len]) { // 右边,取出新的数组数据
int [] temp = null ;
if (data.length % 2 == 0) {
temp = new int [len] ;
} else {
temp = new int [len + 1] ;
}
int foot = 0 ;
for (int x = len ; x < data.length ; x ++) {
temp[foot ++] = data[x] ;
}
return search(sdate,temp) ;
} else if (sdate < data[len]) { // 左边
int [] temp = new int [len] ;
for (int x = 0 ; x < len ; x ++) {
这种代码只是一个娱乐。
3.4、二维数组(理解)
在开发之中,之前使用的数组只有一个索引下标,所以可以简单将其理解为是一个一维数组。一维数组的特点是可以根据一个索引号确定内容,好比如下形式:
二维数组实际上与数据表的形式是完全相同的,有行和列组成,那么要想确认一个数据,必须有行和列的编号,所以二维数组的组成如下:
现在要想确定987这个数据,那么就需要行索引为2,列索引也为2才可以定位到。
对于二维数组的定义有两种语法结构:
·动态初始化:
实际上可以发现,二维数组的本质就是在数组里面继续嵌套了其他的数组。
范例:观察二维数组使用
}
面试题:实现二维数组转置
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data [][] = new int [][] {
{1,2,3} ,{4,5,6},{7,8,9}
} ;
for (int x = 0 ; x < data.length ; x ++) { // 控制数组行
for (int y = 0 ; y <= x ; y ++) {
if (x != y) { // 交换
int temp = data[x][y] ;
data[x][y] = data[y][x] ;
data[y][x] = temp ;
}
}
}
for (int x = 0 ; x < data.length ; x ++) { // 控制数组行
for (int y = 0 ; y < data[x].length ; y ++) {
System.out.print(data[x][y] + "、") ;
}
System.out.println() ;
}
}
}
这些都属于对于数组的逻辑控制,本身只能够作为扩充你大脑思维使用的东西,对实际的开发帮助不大。
3.5、数组与方法
在数组与方法进行传递的操作过程之中,实际上就属于引用传递。在数组与方法间的操作中,主要考虑两种形式:方法接收数组,另外一种就是方法返回数组。不管是接收还是返回,最终操作的一定是一个堆内存的使用。
范例:利用方法来接收数组
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int [] data = new int [] {1,3,5,7,9} ; // 是一个数组
printArray(data) ;
}
public static void printArray(int temp[]) { // 接收一个数组
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ; // 换行
}
}
整个的操作过程与数组的引用接收是完全一样的。但是这个时候的代码只是针对于当前的数组内容进行了简单的输出,于是下面可以进一步的加强,例如:设置一个方法,在此方法之中实现数据的乘2操作。
范例:方法里面修改数组内容
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int [] data = new int [] {1,3,5,7,9} ; // 是一个数组
inc(data) ; // int arr [] = data ;
printArray(data) ;
}
public static void inc(int arr[]) { // 负责数组内容的改变
for (int x = 0 ; x < arr.length ; x ++) {
arr[x] = arr[x] * 2 ; // 数组中的每个元素乘后保存
}
}
public static void printArray(int temp[]) { // 接收一个数组
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ; // 换行
}
}
在大部分情况下,如果某一个方法可以接收的数据类型是引用数据类型,那么往往都不需要设置返回值。
范例:数据排序操作(理解)
面对数组中发现可以保存有多个数据,但是在很多时候(例如:二分查找法)那么必须针对于数组中的数据进行排序,所以这个时候就需要进行一些复杂的处理。
通过分析可以发现,排序并不是一次完成的,而是需要多次,也就是说至少需要“数组长度- 1”次。public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data [] = new int [] {89,10,20,1,0,2,8,3,90,67,9,7} ;
for (int x = 0 ; x < data.length - 1; 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 ;
}
}
System.out.print("第" + x + "次排序:") ;
printArray(data) ;
}
}
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 [] {89,10,20,1,0,2,8,3,90,67,9,7} ;
sort(data) ; // 客户端调用越简单越好
printArray(data) ;
}
public static void sort(int arr[]) { // 单独完成了一个数组的排序操作
for (int x = 0 ; x < arr.length - 1; x ++) {
for (int y = 0 ; y < arr.length - 1 ; y ++) {
if (arr[y] > arr[y + 1]) {
int temp = arr[y] ;
arr [y] = arr [y + 1] ;
arr [y + 1] = temp ;
}
}
}
}
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 [] {89,10,20,1,0,2,8,3,90,67,9,7} ;
int newArr [] = new int [data.length] ; // 声明一个与原始数组大小相同的数组
int foot = data.length - 1 ; // 定义原始数组的操作脚标
for (int x = 0 ; x < newArr.length ; x ++) {
newArr[x] = data[foot --] ; // 逆序保存
}
data = newArr ; // 改变原始数组引用
printArray(data) ;
}
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 [] {89,10,20,1,0,2,8,3,90,67,9,7} ;
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 [] = init() ;
printArray(data) ;
}
public static int [] init() { // 返回一个数组
return new int [] {89,10,20,1,0,2,8,3,90,67,9,7} ;
}
public static void printArray(int temp[]) { // 接收一个数组
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ; // 换行
}
}
如果返回的是引用数据类型,那么返回null语法上是没有任何问题的。
3.6、与数组有关的类库
Java本身提供有一系列的开发类库帮助用户进行代码编写,那么下面首先给出两个基本的数组操作方法1、数组拷贝:以下的语法与原始定义有出入,以后再细化;
·拷贝:System.arraycopy(原始数组,原始数组开始点,目标数组,目标数组的开始点,拷贝长度)。范例:数组拷贝
·原始数组1:1、2、3、4、5、6、7、8、9;
·原始数组2:10、20、30、40、50、60、70、80、90;
·希望实现数组2的部分内容替换掉数组1的内容:1、60、70、80、5、6、7、8、9。
public class ArrayDemo { // 程序必须有类
public static void main(String args[]) {
int data1 [] = new int [] {1,2,3,4,5,6,7,8,9} ;
int data2 [] = new int [] {10,20,30,40,50,60,70,80,90} ;
System.arraycopy(data2,5,data1,1,3) ;
printArray(data1) ;
}
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 [] {89,10,20,1,0,2,8,3,90,67,9,7} ;
java.util.Arrays.sort(data) ;
这种方式是在代码实际开发中使用的,千万要记住,别在面试里完成。
4、总结
1、数组属于引用数据类型;
2、数组属于线性的存储结构,里面的内容可以根据索引线性操作;
3、数组里面容易出现面试题。
5、复习
1、每一个概念反复去看;
2、程序代码必须可以做到不用脑子直接写;
3、对于一些复杂的程序:
·输出三角形、乘法口诀表;
·二维数组转置、数组排序、数组转置、二分查找。