概述
定义:多个相同类型的数据(元素)按一定顺序排列的集合,并使用一个名字命名(数组名),通过编号(索引)的方式对这些数据进行统一管理。这些数据的个数叫做数组的长度
特点:
- 数组是引用数据类型的变量,而数组中的元素可以是任意类型的变量,包括基本数据类型和引用数据类型
- 创建一个数组对象时会在内存中开辟一整块连续的空间,数组名引用的是这块连续空间的首地址
一旦数组的长度确定,就不能修改
- 通过索引的方式可以快速获取到数组中的指定元素
分类:
- 按照维数:一维数组、二维数组、三维数组...
- 按照元素类型:基本数据类型元素数组、引用数据类型元素数组
一维数组的使用
package com.atguigu.java;
public class ArrayTest {
public static void main(String[] args) {
int[] ids; // 声明一个int类型的数组变量,它是引用数据类型
// 1,数组初始化
// 静态初始化:创建一个int类型的数组对象,并同时进行数组的初始化和数组元素的赋值操作
ids = new int[]{1001,1002,1003,1004};
// 动态初始化:创建一个指定长度的String类型的数组对象,只进行赋值操作,初始化放在之后执行
String[] names = new String[5];
// 创建数组的错误写法,创建数组只有静态和动态两种方法
// int[] arr1 = new int[];
// int[5] arr2 = new int[5]; 声明一个数组变量时[]中不能写数字
// int[] arr3 = new int[3]{1,2,3};
// 总结:数组一旦初始化完成,其长度就确定了
// 创建数组,无非就是创建一个“明确”的数组对象然后将它赋值给对应类型的引用数据类型变量
// “明确”是指它需要告诉JVM,要为这个数组分配多大的内存空间,所以不论哪种创建数组的方式,数组长度是必须指定的
// 而创建数组对象可以直接写出它要存储哪些元素,此时数组的长度也确定了,所以不需要在[]中写长度
// 或者是创建一个指定长度的数组对象,此时未写出它要存储的元素,可以之后再加上
// 2,通过索引访问数组元素
// 数组索引从0开始,到数组长度-1结束
// 初始化names数组
names[0] = "wangming";
names[1] = "wanghe";
names[2] = "zhangxueliang";
names[3] = "sunjulong"; // charAt(0):s
names[4] = "wanghongzhi";
// 获取索引为2的数组元素
String name = names[2];
// 3,获取数组长度:通过数组对象的属性length
System.out.println(ids.length); // 4
// 4,遍历数组元素
// Ctrl+/注释或取消注释选中内容,Ctrl+shift+/多行注释选中内容,Ctrl+shift+\取消多行注释
for(int i = 0 ; i < names.length ; i++) {
System.out.println(names[i]);
}
}
}
package com.atguigu.java;
public class ArrayTest1 {
public static void main(String[] args) {
// 5,动态方式创建数组时,数组元素的默认初始化值
int[] arr = new int[4];
for(int i = 0 ; i < arr.length ; i++) {
System.out.println(arr[i]); // 0 0 0 0
}
// 对于整型的数组元素,默认初始化值为0
// 对于浮点型的数组元素,默认初始化值为0.0
// 对于布尔型的数组元素,默认初始化值为false,其对应的也是0,true对应1
// 对于char型的数组元素,默认初始化值为0
char[] arr1 = new char[3];
for(int i = 0 ; i < 3 ; i++) {
System.out.println(arr1[i]); // 输出一个竖着的长方形符号,一些地方输出一个空格,但这不是真正的空格,空格符号对应的ASCII码不为0
}
if(arr1[0] == 0) {System.out.println("你好");} // 说明char类型数组的默认初始值确实为0
// 对于String引用类型的数组元素,默认初始化值为null,不是这个元素没有引用任何一个对象
String[] arr2 = new String[3];
System.out.println(arr2[0]); // null
}
}
内存的主要结构
6,一维数组的内存解析
arr[1] = "刘德华";时,并不是直接就赋值,而是创建了一个String类型的对象,这个对象放在常量池中,然后将它赋值给String引用类型变量arr[1]。当main()方法执行完,栈中的所有局部变量也都消失,堆中的对象没有引用便会被垃圾回收。
例题
package com.atguigu.java;
import java.util.Scanner;
public class ArrarTest2 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("请输入学生人数:");
int number = scan.nextInt();
int[] scores = new int[number];
int maxScore = 0;
System.out.println("请输入" + number + "个学生成绩:");
for(int i = 0 ; i < scores.length ; i++) {
scores[i] = scan.nextInt();
if(maxScore < scores[i]) {maxScore = scores[i];} // 找出输入的最大值
}
char level;
for(int i = 0 ; i < scores.length ; i++) {
if(maxScore - scores[i] <= 10) {
level = 'A';
}else if(maxScore - scores[i] <= 20) {
level = 'B';
}else if(maxScore - scores[i] <= 30) {
level = 'C';
}else {
level = 'D';
}
System.out.println("student " + i +
" score is " + scores[i] + ",grade is " + level);
}
}
}
二维数组的使用
当一个数组的每个元素都是一个数组时,这个数组就是二维数组
package com.atguigu.java;
public class ArrayTest3 {
public static void main(String[] args) {
// 1,二维数组的声明和初始化
// 静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
// 动态初始化1
String[][] arr2 = new String[3][2]; // 一个长度为3的数组,它的每个元素都是一个长度为2的数组
// 动态初始化2
String[][] arr3 = new String[3][]; // 一个长度为3的数组,它的每个元素都是数组,但这个数组长度未确定
// 错误的定义
// String[][] arr4 = new String[][4];
// String[4][3] arr5 = new String[][];
// int[][] arr6 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};
// 其他定义数组的正确方式
int arr4[][] = new int[][]{{1,2,3},{4,5},{6,7,8}};
int[] arr5[] = new int[][]{{1,2,3},{4,5},{6,7,8}};
int arr6[][] = {{1,2,3},{4,5},{6,7,8}};
// 对于一维数组也可以省略
int[] arr7 = {1,2,3,4}; // 类型推断,前面指定了是一个int型数组,所以后面创建的不可能是一个其他类型的
// 但这样是错的,换行了编译器不能推断出来
// int[] arr8;
// arr8 = {1,2,3,4};
}
}
访问二维数组元素
package com.atguigu.java;
public class ArrayTest4 {
public static void main(String[] args) {
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
String[][] arr2 = new String[3][2];
String[][] arr3 = new String[3][];
// 访问二维数组元素。三维数组也类似,有三个[]
System.out.println(arr1[0][1]); // 2
System.out.println(arr2[1][1]); // null
// System.out.println(arr3[1][0]); // 报错,因为作为数组元素的数组还没有创建
arr3[1] = new String[4]; // 创建一个数组赋值给外层数组的第一个元素
System.out.println(arr3[1][0]); // null
// 获取二维数组长度
System.out.println(arr1.length); // 3,arr1可以看成是一个特殊的一维数组,只不过它
// 的元素也是数组,它有3个元素,所以它的长度是3。就像是一个String类型的数组,
// 它的每个元素也是包含多个char类型值
System.out.println(arr1[0].length); // 3
System.out.println(arr1[1].length); // 2
// 遍历二维数组
for(int i = 0 ; i < arr1.length ; i++) {
for(int j = 0 ; j < arr1[i].length ; j++) {
System.out.print(arr1[i][j] + " ");
}
System.out.println();
}
}
}
二维数组元素的默认初始化值
package com.atguigu.java;
public class ArrayTest5 {
public static void main(String[] args) {
// 二维数组元素的默认初始化值
// 把二维数组的arr[0]、arr[1]叫做外层元素,arr[0][0]、arr[1][0]叫做内层元素
int[][] arr = new int[4][3];
System.out.println(arr[0]); // [I@15db9742,arr[0]是一个数组对象的引用。[I表示这是一个int类型的一维数组,一维数组的地址(@)是15db9742
System.out.println(arr[0][0]); // 0
System.out.println(arr); // [[I@6d06d69c,arr是一个二维数组对象的引用。[[I表示这是一个int类型的二维数组,二维数组的地址(@)是6d06d69c
float[][] arr1 = new float[4][3];
System.out.println(arr1[0]); // [F@7852e922,arr1[0]是一个数组对象的引用。[F表示这是一个float类型的一维数组,一维数组的地址(@)是15db9742
System.out.println(arr1[0][0]); // 0.0
String[][] arr2 = new String[4][2];
System.out.println(arr2[1]); // [Ljava.lang.String;@4e25154f,一个String类型的数组对象的引用及其地址
System.out.println(arr2[1][1]); // null
double[][] arr3 = new double[4][];
System.out.println(arr3[1]); // null,一个数组对象的引用,它的值要么是一个地址,要么为空
System.out.println(arr3[1][0]); // 编译报错,相当于取出一个不存在的数组中的值
}
}
二维数组内存结构
当main()方法执行完,arr4消失,它引用的对象被回收,该对象存储的两个数组对象的引用消失,接着两个数组对象被回收
练习1
package com.atguigu.java;
public class YangHui {
public static void main(String[] args) {
int[][] yangHui = new int[10][];
for(int i = 0 ; i < yangHui.length ; i++) {
yangHui[i] = new int[i + 1];
yangHui[i][0] = yangHui[i][i] = 1;
// if(i > 1) { // 可以去掉,i <= 1时下面的for不会执行
for(int j = 1 ; j < yangHui[i].length - 1 ; j++) {
yangHui[i][j] = yangHui[i-1][j] + yangHui[i-1][j-1];
}
// }
}
for(int i = 0 ; i < yangHui.length ; i++) {
for(int j = 0 ; j < yangHui[i].length ; j++) {
System.out.print(yangHui[i][j] + " ");
}
System.out.println();
}
}
}
练习2
package com.atguigu.java;
public class ArrayTest {
public static void main(String[] args) {
// 一个长度为6的数组,每个元素的值是随机的在1-30之间且不能重复
int[] arr = new int[6];
int a;
int j;
for(int i = 0 ; i < arr.length ; i++) {
while(true) {
// a = (int)Math.random() * 30;
a = (int)(Math.random() * (7 - 1 + 1));
System.out.println(a);
for(j = 0 ; j < i ; j++) {
if(arr[j] == a) break;
}
if(arr[j] != a) {
arr[i] = a;
break;
}
}
}
for(int i = 0 ; i < arr.length ; i++) {
System.out.print(arr[i] + " ");
}
}
}
数组的复制
package com.atguigu.java;
public class Test {
public static void main(String[] args) {
String[] arr = new String[]{"JJ","DD","MM","BB","GG","AA"};
// 数组的复制
String[] arr1 = new String[arr.length];
for(int i = 0 ; i < arr.length ; i++) {
arr1[i] = arr[i];
}
// 数组的反转
for(int i = 0 ; i < arr.length / 2 ; i++) { // 进行数组长度一半的交换元素的次数
String temp = arr[i];
arr[i] = arr[arr.length - 1 - i]; // 与索引为i的元素对应的索引是数组长度-1-i
arr[arr.length -1 -i] = temp;
}
for(int i = 0 ; i < arr.length ; i++) {
System.out.print(arr[i] + '\t');
}
}
}
线性查找数组中的元素
package com.atguigu.java;
public class Test {
public static void main(String[] args) {
String[] arr = new String[]{"JJ","DD","MM","BB","GG","AA"};
// 线性查找
String dest = "BB";
boolean isFlag = true;
for(int i = 0 ; i < arr.length ; i++) {
if(dest.equals(arr[i])) {
System.out.println("数组中有" + dest + ',' + "索引为" + i);
isFlag = false;
break;
}
}
if(isFlag) {System.out.println("数组中没有" + dest);}
}
}
二分查找
package com.atguigu.java;
public class Test {
public static void main(String[] args) {
// 二分法查找,前提是数组必须有序
int[] arr = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int dest = -3;
int head = 0;
int end = arr.length - 1;
boolean isFlag = true; // 如果没有找到元素,这个标记就是true
while(head <= end) {
int middle = (head + end) / 2;
if(dest == arr[middle]) {
System.out.println("找到了指定元素,位置是" + middle);
isFlag = false;
break;
}else if(arr[middle] > dest) {
end = middle - 1; // 将middle前一个位置的值赋给end,因为索引为middle的元素已经比较过了
}else {
head = middle + 1;
}
}
if(isFlag) {System.out.println("没有找到");}
}
}
/* 1,条件为什么是head <= end,head有可能超过end吗?
* 2,重置head和end时可以赋值middle吗?
* 每次二分操作时,如果是将middle赋给head或end,对于在上面的数组中查找107的操作
* 最后的结果是head=7,end=8的死循环。而head = middle + 1和end = middle - 1
* 则不会出现这种情况,这样赋值也是显而易见的,因为索引为middle的元素已经比较过了
* 再次操作107时,最后head会超过end,这表示整个数组已经找完了,没有107,所以将这个
* 条件作为循环条件
*/