一、基本概念

数组指的就是一组相关变量的集合。例如:如果现在要想定义100个整型变量,如果按照传统的思路:

int i1,i2,……i100,一共写100个变量。

这100个变量满足逻辑要求,但是没有任何的逻辑的控制关系,完全独立,那么就会出现对象不方便管理的情况。这种情况下,就可以利用数组来解决此类问题,而数组本身也属于引用数据类型。数组的定义语法如下:

·  声明并开辟数组:数据类型 数组名称[] = new 数据类型[长度]

                                数据类型  [] 数组名称 = new 数据类型[长度]

· 分步完成:

              |-声明数组:数据类型 数组名称 [] = null;

              |-开辟数组:数组名称 =new 数据类型[长度]。

当数组开辟空间之后,那么可以采用“数组名称[下标|索引]”的形式进行访问,但是所有数组的下标都是从0开始的,即:如果是3个长度的数组,那么下标的范围:0 ~ 2(0、1、2一共是3个内容)。如果访问的时候超过了数组的允许下标的长度,那么或出现数组越界异常(ArrayIndexOutOfBoundsException)。

以上给出的数组定义结构使用的是动态初始化的方式,即:数组会首先开辟内存空间,但是数组中的内容都是其对应数据类型的默认值。

由于数组是一种顺序的结构,并且数组的长度都是固定的,那么可以使用循环的方式输出,很明显需要知道for循环,而且在Java里面为了方便数组输出提供有一个“数组名称.length”的属性可以取得数组长度。

范例:定义数组

public class ArrayDemo {
	public static void main(String[] args) {
		// 声明并开辟了一个3个长度的数组
		int data[] = new int[3];
		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) {
		// 声明并开辟了一个3个长度的数组
		int data[] = null;
		data = new int[3];
		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) {
		// 声明并开辟了一个3个长度的数组
		int data[] = new int[3];
		data[0] = 10;
		data[1] = 20;
		data[2] = 30;
		int temp[] = data;
		temp[0] = 99;
		for (int x = 0; x < data.length; x++) {
			System.out.println(data[x]);
		}
	}
}

以上数组的定义格式严格来讲是属于动态初始化,它的操作特点:先开辟数组空间,而后为数组中的内容进行赋值。那么在数组定义之中还提供有静态初始化的操作,即:数组定义的同时就设置好了相应的数据内容,格式如下:

  • 格式一:简化格式:数据类型 数组名称 [] = {值,值,……};
  • 格式二:完整格式:数据类型 数组名称 [] = new 数据类型 [ ] {值,值,……};

范例:数组的静态初始化

public class ArrayDemo {
	public static void main(String[] args) {
		// 声明并开辟了一个3个长度的数组
		int data[] = new int[]{1,2,3,4,5};
		for (int x = 0; x < data.length; x++) {
			System.out.println(data[x]);
		}
	}
}

在实际工作之中,数组是会存在的,但是它的内容大部分情况下都是通过传递的数据而动态生成的,很少会出现这种先开辟数组而后去使用的情况。

虽然数组支持顺序的数据访问操作,但是数组有一个最大的缺点——长度不能够被改变,所以正因为如此,在实际的开发之中,才不会直接的应用数组,但是会用到数组的概念。


二、二维数组

在之前的数组里面只有一个“[]”,所以此类数组就是一个普通的数组,或者称为一维数组。如果现在要想描述更多的数据,可以使用二维数组,后面有两个“[]”。

一维数组严格来讲就是一行,类似于如下的形式:

索引

0

1

2

3

4

5

6

7

8

数据

90

23

324

1234

423

435

3456

345

2423

如果在一维数组里面要找到一个数据,只需要确定一个索引就够了。

二维数组就是一张数据表,有行有列,类似于如下的形式:

索引

0

1

2

3

4

5

6

7

8

0

90

23

324

1234

423

435

3456

345

2423

1

325

345

56

7

87

24

78

65

5

2

7658

79

234

887

53

65

243

71

76

如果要想在二维数组里面确认一个数据,需要行和列一起定位,例如:数字7的索引位置:行1列3“[1][3]”。而对于二维数组的定义语法有如下两类:

  •  动态初始化:数据类型 数组名称[] [] = new 数据类型[行的个数][列的个数]
  •  静态初始化:数据类型 数组名称[] [] = new 数据类型[][]{{值,值,值,……},{},……}

通过定义结构发现,所谓的二维数组实际上就是将多个一维数组变为了一个大的数组,并且为每一个一维数组设置了一个行号而已。

范例:观察二维数组

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 < data[x].length; y++) {
				System.out.print(data[x][y] + "\t");
			}
			System.out.println();
		}
	}
}

随着开发的发展,在直接编写的代码里面会很少出现二维数组的概念。


三、数组与方法参数的传递

在之前所编写的所有方法发现传递的数据几乎都使用的是基本数据类型,那么除了基本数据类型之外,也可以传递数组,但是如果传递的数组,请千万记住,观察内存分配图。

范例:一个数组传递的程序

public class ArrayDemo {
	public static void main(String[] args) {
		int data[] = new int[] { 1, 2, 3 };
		change(data);
		for (int x = 0; x < data.length; x++) {
			System.out.println(data[x]);
		}
	}

	// 此方法定义在主类中,并由主方法直接调用
	public static void change(int temp[]) {
		for (int x = 0; x < temp.length; x++) {
			temp[x] *= 2;
		}
	}
}
public class ArrayDemo {
	public static void main(String[] args) {
		int data[] = new int[] { 1, 2, 3 };
		int temp[] = data;
		for (int x = 0; x < temp.length; x++) {
			temp[x] *= 2;
		}
		for (int x = 0; x < data.length; x++) {
			System.out.println(data[x]);
		}
	}
}

在进行数组的引用传递的过程之中,方法对数组的修改一定会影响到原始数据。

范例:实现一个数组排序

数组的排序操作在笔试之中经常被问到,下面给出(升序)排序的基本原理:

  • 原始数据:2、1、9、0、、5、3、7、6、8
  • 第一次排序:1、2、0、5、3、7、6、8、9
  • 第二次排序:1、0、2、3、5、6、7、8、9
  • 第三次排序:0、1、2、3、5、6、7、8、9

以上只是给出了排序的基础原理过程,但是会根据数据的不同会出现不同的排序次数,但是不管有多少个数据,总的排序次数不会超过数组的长度。所以只要排序的次数达到了“长度*长度”,那么所有的数据一定可以排序成功。

  • 基础的实现:
public class ArrayDemo {
	public static void main(String[] args) {
		int data[] = new int[] { 2, 1, 9, 0, 5, 3, 7, 6, 8 };
		print(data);
		// 外层控制总体排序的次数
		for (int x = 0; x < data.length; x++) {
			// 内层控制每次的排序控制
			for (int y = 0; y < data.length - 1; y++) {
				if (data[y] > data[y + 1]) {
					int t = data[y];
					data[y] = data[y + 1];
					data[y + 1] = t;
				}
			}
		}
		print(data);
	}

	// 专门定义一个输出的功能的方法
	public static void print(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[] { 2, 1, 9, 0, 5, 3, 7, 6, 8 };
		sort(data);// 实现排序
		print(data);
	}

	public static void sort(int arr[]) {
		for (int x = 0; x < arr.length; x++) {
			// 内层控制每次的排序控制
			for (int y = 0; y < arr.length - 1; y++) {
				if (arr[y] > arr[y + 1]) {
					int t = arr[y];
					arr[y] = arr[y + 1];
					arr[y + 1] = t;
				}
			}
		}
	}

	// 专门定义一个输出的功能的方法
	public static void print(int 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;
  • 转置后的数组: 8、7、6、5、4、3、2、1。

如果要想实现转置的操作,有两个思路:

  •  定义一个新的数组,而后将原始数组按照倒序的的方式插入到新的数组中,随后改变原始数组引用。
public class ArrayDemo {
	public static void main(String[] args) {
		int data[] = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
		// 首先定义一个新的数组,长度与原始数组一致
		int temp[] = new int[data.length];
		int foot = data.length - 1;// 控制data数组的索引
		// 对于新的数组按照索引有小到大的顺序循环
		for (int x = 0; x < temp.length; x++) {
			temp[x] = data[foot];
			foot--;
		}
		// 此时temp的内容就是转置后的结果
		// 让data指向temp,而data的原始数据就成为垃圾
		data = temp;
		print(data);
	}

	// 专门定义一个输出的功能的方法
	public static void print(int 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

|-第一次转置:8、2、3、4、5、6、7、1

|-第二次转置:8、7、3、4、5、6、2、1

|-第三次转置:8、7、6、4、5、3、2、1

|-第四次转置:8、7、6、5、4、3、2、1

转换次数:数组长度/2

但是以上给出的数组长度是一个偶数,如果是一个奇数呢?

|-原始数组:  1、2、3、4、5、6、7、

|-第一次转置:7、2、3、4、5、6、1

|-第二次转置:7、6、3、4、5、2、1

|-第三次转置:7、6、5、4、3、2、1

转换次数:数组长度/2

也就是说不管个数是奇数还是偶数,转置的次数计算方式是完全一样的。

public class ArrayDemo {
	public static void main(String[] args) {
		int data[] = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
		reverse(data);
		print(data);
	}

	// 此方法专门实现数组的转置操作
	public static void reverse(int arr[]) {
		int len = arr.length / 2;// 转置的次数
		int head = 0;// 头部索引
		int tail = arr.length - 1;// 尾部索引
		for (int x = 0; x < len; x++) {
			int temp = arr[head];
			arr[head] = arr[tail];
			arr[tail] = temp;
			head++;
			tail--;
		}
	}

	// 专门定义一个输出的功能的方法
	public static void print(int 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

第一次转置:4的索引[0][1]、2的索引[1][0]。行数和列数不同。

1     4     3

2     5     6

7     8     9

第二次转置:

1     4     7

2     5     6

3     8     9

第三次转置:

1     4     7

2     5     8

3     6     9

只有行和列相同的时候才会发生交换的操作。

public class ArrayDemo {
	public static void main(String[] args) {
		int data[][] = new int[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
		reverse(data);
		print(data);
	}

	// 此方法专门实现数组的转置操作
	public static void reverse(int arr[][]) {
		for (int x = 0; x < arr.length; x++) {
			for (int y = x; y < arr[x].length; y++) {
				if (x != y) {// 行和列不同,进行交换
					int temp = arr[x][y];
					arr[x][y] = arr[y][x];
					arr[y][x] = temp;

				}
			}
		}
	}

	// 专门定义一个输出的功能的方法
	public static void print(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();
	}
}

1[x=0][y=0]      2[x=0][y=1]      3[x=0][y=2]

4[x=1][y=0]      5[x=1[y=1]       6[x=1][y=2]

7[x=2][y=0]      8[x=2][y=1]      9[x=2][y=2]

第一次转换(x=0,y=x=0,循环3次)

|-y的第一次循环(x==y,不交换)

1[x=0][y=0]      2[x=0][y=1]      3[x=0][y=2]

4[x=1][y=0]      5[x=1[y=1]       6[x=1][y=2]

7[x=2][y=0]      8[x=2][y=1]      9[x=2][y=2]

|-y的第二次循环(x=0,y=1,判断条件满足,进行交换)

1[x=0][y=0]      4[x=1][y=0]      3[x=0][y=2]

2[x=0][y=1]      5[x=1[y=1]       6[x=1][y=2]

7[x=2][y=0]      8[x=2][y=1]      9[x=2][y=2]

|-y的第三次循环(x=0,y=2,判断条件满足,进行交换)

1[x=0][y=0]      4[x=1][y=0]      7[x=2][y=0]

2[x=0][y=1]      5[x=1[y=1]       6[x=1][y=2]

3[x=0][y=2]      8[x=2][y=1]      9[x=2][y=2]

第二次转换(x=1,y=x=1,循环2次)

|-y的第一次循环(x=1,y=1,不交换)

1[x=0][y=0]      4[x=1][y=0]      7[x=2][y=0]

2[x=0][y=1]      5[x=1[y=1]       6[x=1][y=2]

3[x=0][y=2]      8[x=2][y=1]      9[x=2][y=2]

|-y的第二次循环(x=1,y=2,交换)

1[x=0][y=0]      4[x=1][y=0]      7[x=2][y=0]

2[x=0][y=1]      5[x=1[y=1]       8[x=2][y=1]

3[x=0][y=2]      6[x=1][y=2]      9[x=2][y=2]

第三次转换(x=2,y=x=2,循环1次)

|-y的第一次循环(x=2,y=2,不交换)

1[x=0][y=0]      4[x=1][y=0]      7[x=2][y=0]

2[x=0][y=1]      5[x=1[y=1]      8[x=2][y=1]

3[x=0][y=2]      6[x=1][y=2]      9[x=2][y=2]

以上给出的是一个等行列的数组,但是如果不一样的呢?

1     2      3

4     5      6

开辟一个新的数组,数组的行数是原始数组的列数,数组的列数是原始数组的行数。需要改变原始数组的引用,会产生垃圾。

以上实现了方法接受数组的操作情况,同样方法也可以返回数组。

范例:方法返回数组

public class ArrayDemo {
	public static void main(String[] args) {
		int data[] = init();// 接收数组
		print(data);
		//init()方法返回的是一个数组
		//数组可以直接使用length取得长度
		System.out.println(init().length);
	}

	public static int[] init() {// 方法返回数组
		return new int[] { 1, 2, 3 };
	}

	public static void print(int temp[]) {
		for (int x = 0; x < temp.length; x++) {
			System.out.print(temp[x] + "、");
		}
		System.out.println();
	}
}

 重点关注方法的返回值即可。


四、数组的相关操作方法

Java本身针对于数组是由提供类库支持的,下面来说两个与数组有关的操作方法。

1、数组拷贝

可以将一个数组的部分内容拷贝到另外一个数组之中;

语法:System.arraycopy(原数组名称,原数组拷贝开始索引,目标数组名称,目标数组拷贝开始索引,长度)

范例:实现数组拷贝

  •  数组A:1、2、3、4、5、6、7、8
  • 数组B:11、22、33、44、55、66、77、88
  • 要求拷贝后的数组B:11、22、5、6、7、66、77、88
public class ArrayDemo {
	public static void main(String[] args) {
		int dataA[] = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
		int dataB[] = new int[] { 11, 22, 33, 44, 55, 66, 77, 88 };
		System.arraycopy(dataA, 4, dataB, 2, 3);
		print(dataB);
	}

	public static void print(int temp[]) {
		for (int x = 0; x < temp.length; x++) {
			System.out.print(temp[x] + "、");
		}
		System.out.println();
	}
}

2、数组排序

之前给出了排序的基本操作,但是在开发里面如果要进行排序只需要使用如下代码即可。

语法:java.util.Arrays.sort(数组名称);

范例:实现排序

import java.util.Arrays;

public class ArrayDemo {
	public static void main(String[] args) {
		int data[] = new int[] { 3, 6, 2, 1, 8, 0 };
		Arrays.sort(data);
		print(data);
	}

	public static void print(int temp[]) {
		for (int x = 0; x < temp.length; x++) {
			System.out.print(temp[x] + "、");
		}
		System.out.println();
	}
}

五、对象数组

数组是引用类型,而类同样是引用类型,所以如果是对象数组的话表示一个引用类型里面嵌套了其它的引用类型。

在之前使用的数组都属于基本数据类型的数组,但是所有的引用数据类型也同样可以定义数组,这样的数组称为对象数组。如果要想定义对象数组(以类为例),可以采用如下的形式完成:

  •  动态初始化:开辟之后对象数组的内容都是null值。

     |-声明并开辟对象数组:类名称 对象数组名称 [] = new 类名称[长度];

     |-分步完成:

          |-声明对象数组:类名称 对象数组名称 [] = null;

          |-开辟对象数组:对象数组名称 = new 类名称[长度];

  • ·静态初始化:类名称 对象数组名称[] = new 类名称[] {实例化对象,实例化对象,……};

范例:对象数组的动态初始化

class Book {
	private String title;
	private double price;;

	public Book(String t, double p) {
		title = t;
		price = p;
	}

	// setter、getter、无参构造略
	public String getInfo() {
		return "书名:" + title + "价格:" + price;
	}
}

public class ArrayDemo {
	public static void main(String[] args) {
		// 开辟了一个3个长度的对象数组
		Book books[] = new Book[3];
		// 对象数组中的每个数据都需要分别实例化
		books[0] = new Book("Java", 79.8);
		books[1] = new Book("JSP", 73.8);
		books[2] = new Book("Android", 72.8);
		for (int x = 0; x < books.length; x++) {
			System.out.println(books[x].getInfo());
		}
	}
}

对象数组实际上就是将多个对象交给数组统一管理。

范例:使用静态初始化

class Book {
	private String title;
	private double price;;

	public Book(String t, double p) {
		title = t;
		price = p;
	}

	// setter、getter、无参构造略
	public String getInfo() {
		return "书名:" + title + "价格:" + price;
	}
}

public class ArrayDemo {
	public static void main(String[] args) {
		// 开辟了一个3个长度的对象数组
		Book books[] = new Book[] { new Book("Java", 79.8), 
				new Book("JSP", 73.8), 
				new Book("Android", 72.8) };
		for (int x = 0; x < books.length; x++) {
			System.out.println(books[x].getInfo());
		}
	}
}

一般而言,使用对象数组的时候只会定义成一维。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐