06.JAVASE 数组 & Arrays工具类
定义:存放相同数据类型的一组数据的容器
1.数组初始化 & 使用
1.1格式
- 声明:数据类型 [ ] 数组名; (eg:int[ ] a;)表示向计算机申请在内存开辟连续的int类型的空间
- 赋值:数据类型 [ ] 数组名 = new 数据类型 [ 数据长度 ];
- 使用
1.2数组的访问方式
数组名[ 下标 / 索引 / index]
索引范围:[ 0 - 数组长度 - 1 ]
1.3数组的快速初始化
-
静态初始化:初始化时不需要定义数组的长度,但要指定元素的值,系统根据定义的元素个数得出数组的长度
//格式一 数组类型[ ]数组名 = new 数组类型[ ] { 元素一,元素二 … } int[] arr = new int[ ]{11, 22, 33, 44, 55}; //一般适用于匿名对象
//格式二 数据类型[ ] 数组名 = { 元素一,元素二 … } int[ ] arr = {11, 22, 33, 44, 55};
-
动态初始化:在初始化的同时确定数组大小,但不指定元素的值,后期通过动态指定,系统会根据数据类型分配相应的默认值
数据类型[] 数组名 = new 数据类型[数组大小] double[] scores = new double[30];
动态初始化给值时:需要有new: arr = new int[]{1,2,3.4}; //不能直接 arr = {1,2,3.4}; //以为需要在堆区开辟空间,获取地址值
分配规则:
数据类型 默认值 byte short int long 0 float double 0.0 char ‘\u0000’ 就是一个空字符 boolean false 引用类型 null -
如何快速初始化数组?、
- 通过静态初始化(快速初始化,直接赋值)
- 通过键盘输入(生产环境)
- 通过随机数(测试)
2.注意事项
2.1数组的特点
- 数组是一个容器
- 数组是变量,需满足声明,再赋值
- 数组是引用数据类型,赋值不同于基本数据类型,不能存放数值,只能存放地址(地址值的获取必须由系统分配空间,并且随机分配地址)
- 数组中每一个元素的数据类型可以是八大基本数据类型,同时也可以是引用数据类型
int i = 10;
//int[] arr;
//arr = 10;
//这是错误的,引用数据类型(数组在内)不能存放数值,只能存放地址
//而想存放地址,就必须在系统开辟空间
//堆区:存放new出来的东西 ( eg:int[ ] arr = new int[3]; Scanner input = new Scanner(System.in);)
//所以需要new int[数组长度]来获取地址
2.2问题
-
为什么数组中每一个元素会有默认值?
----因为不同于栈区变量没有默认值,堆区每个变量都有默认值
-
NullPointerException是如何出现的?(空指针异常)
int[] arr = null; System.out.println(arr); //产生原因:null是默认值,对象没有分配地址,直接去访问的话,会找不到地址,即空指针异常 //解决方法:给对象分配地址,即 new
-
java.lang.ArrayIndexOutOfBoundsException(数组越界)
----超出了索引范围:[ 0 - 数组长度 - 1 ]
2.3垃圾回收机制
- System.gc();// 启动垃圾回收器
- System.runFinalization();// 通知垃圾回收期执行回收垃圾的方法
- 但是显示回收不一定起作用,因为可能正在在回收别的垃圾
3.值传递 & 引用传递
传递: 实参赋值给形参
形参可以是八大基本数据类型和引用数据类型
3.1" == "比较问题
- 基本数据类型比较时,比较的是数值
- 引用数据类型比较时,比较的是地址值
形参 | 传递本质 | |
---|---|---|
值传递 | 八大基本数据类型 | 传递数值本身 |
引用传递 | 引用数据类型 | 传递地址值 |
//值传递
public class ArrayDemo06 {
public static void main(String[] args) {
int a = 10;
System.out.println("main方法中a的值: " + a);//结果是:10
change(a);//结果是:15
System.out.println("main方法中调用change方法后a的值: " + a)//结果是:10
}
public static void change(int a) {
a += 5;
System.out.println("change方法的a的值: " + a);
}
//调用方法时,方法在栈区开辟了新的空间压栈,
//和主函数的main不在用一个空间内,两个int a并不是同一个值
//所以互不影响
//引用传递
public class ArrayDemo06 {
public static void main(String[] args) {
int[] arr = new int[]{ 11, 22, 33 };
System.out.println("main方法中数组arr每一个元素的值: " + Arrays.toString(arr));//结果: [11, 22, 33]
changeArr(arr);//结果:[11, 666, 33]
System.out.println("main方法经过方法调用后中数组arr每一个元素的值: " + Arrays.toString(arr));//结果:[11, 666, 33]
}
public static void changeArr(int[] arr) {
arr[1] = 666;
System.out.println("changeArr方法中数组每一个元素的值: " + Arrays.toString(arr));
}
}
//因为方法调用的参数arr是同一个变量,所以地址值是相同,在堆区指向的空间的相同的,对arr[1]的重新赋值,会导致元素值发生改变,所以再次打印的数组是已经改变的数组。
//多个引用指向同一个堆区空间的问题
public class ArrayDemo07 {
public static void main(String[] args) {
int[] arr = { 11, 22, 33 };
int[] arr2 = { 44, 55, 66 };
int[] arr3 = arr;//!!!!
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arr2));
System.out.println(Arrays.toString(arr3));
arr[1] = 666;
System.out.println(Arrays.toString(arr)); // [11, 666, 33]
System.out.println(Arrays.toString(arr2)); // [44, 55, 66]
System.out.println(Arrays.toString(arr3)); // [11, 666, 33]
}
}
//arr3与arr地址相同,所以任何一个引用如果修改了堆区的内容,那么必然会影响到所有的引用
4.内存中的堆区
堆区的特点:
- 堆区每个变量都有默认值(1.3初始化分配规则)
- new出来的东西系统都会在堆区开辟空间,并随机赋上地址值
- 在堆区的对象使用完毕后,并不会马上消失,需要等待垃圾回收器空闲的时候自动回收
5.数组的功能实现
-
循环输出数列的值
/* * 功能: 循环输出数列的值 * 参数列表: int[] arr * 返回值类型: void * 方法名: printArray */ public static void printArray(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } }
-
求数列中所有数值的和
/* * 功能: 求数列中所有数值的和。 * 参数列表: int[] arr * 返回值类型: int * 方法名: getSum */ public static int getSum(int[] arr) { int sum = 0; for (int i = 0; i < arr.length; i++) { sum += arr[i]; } return sum; }
-
求出最大值
/* * 功能: 求出最大值。 * 参数列表: int[] arr * 返回值类型: int * 方法名: getMaxValue */ public static int getMaxValue(int[] arr) { // 假定第一个数为最大值 int max = arr[0]; // 遍历数组中的每一个元素 for (int i = 0; i < arr.length; i++) { // 获取到每一个元素i // 将元素和max进行比较 // 如果元素比假定的max还要大,那么就将该数设置为max if (arr[i] > max) { max = arr[i]; } } return max; }
-
将数组倒置
/* * 功能: 将数组倒置 * 参数列表: int[] arr * 返回值类型: void * 方法名: reverseArray */ public static void reverseArray(int[] arr) { for (int i = 0; i < arr.length / 2; i++) { // arr[i]和arr[arr.length - 1 - i] 交换位置 int temp = arr[i]; arr[i] = arr[arr.length - 1 - i]; arr[arr.length - 1 - i] = temp; } }
-
数据查找(基本查找boolean型)
/* * 功能:基本查找 * 参数列表: int[] arr, int num * 返回值类型: boolean * 方法名: basicSearch */ public static boolean basicSearch(int[] arr, int num) { boolean flag = false; for (int i = 0; i < arr.length; i++) { if (num == arr[i]) { flag = true; break; } } return flag; }
-
数据查找(基本查找int型)
/* * 功能:基本查找 * 参数列表: int[] arr, int num * 返回值类型: int * 方法名: basicSearch2 */ public static int basicSearch2(int[] arr, int num) { int index = -1; for (int i = 0; i < arr.length; i++) { if (num == arr[i]) { index = i; break; } } return index; }
-
二分法查找
/* * 功能: 查找某个数在数列中的位置 * 返回值类型: int * 参数列表: int[] arr, int num * 方法名: binarySearch */ public static int binarySearch(int[] arr, int num) { // 1.定义最小索引和最大索引 int min = 0; int max = arr.length - 1; // 2.计算中间索引 int mid = (min + max) / 2; // 3.拿中间索引所对应的数值和需要查找的数据进行比较 // 由于不知道比较多少次,选择while循环 // arr[mid] != mid 就继续循环 while (arr[mid] != num) { if (arr[mid] > num) { // 在左边找 max = mid - 1; } else if (arr[mid] < num) { // 在右边找 min = mid + 1; } // 如果最小索引超过了最大索引,说明没找到 if (min > max) { return -1; } // 重新计算中间索引 mid = (min + max) / 2; } // 如果中间索引所对应的数值和需要查找的数据相等,就直接返回中间索引 return mid; }
6.八大排序
排序算法: 冒泡 选择 插入 堆 希尔 快速排序 归并排序 基数排序
-
冒泡排序:相邻两个元素进行比较,前面的数大于后面的数,交换两个数
for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j+1]) { int temp = 0; temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } System.out.println("冒泡排序后: " + Arrays.toString(arr)); } //1.相邻两个元素进行比较,前面的数大于后面的数,交换两个数 //2.一个比较了 arr.length - 1趟 //3.每一趟比上一趟少比较一次
-
选择排序:从最左边开始,依次和右边所有元素比较,较小的数往前移动,每一趟比较完后,最小数出现在最前面
for (int i = 0; i < arr.length - 1; i++) { for (int j = i+1; j < arr.length; j++) { if (arr[i] > arr[j]) { int temp = 0; temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } System.out.println("选择排序后: " + Arrays.toString(arr)); } //1.将指定的元素依次和后面每一个元素进行比较 //2.一共比较了arr.length - 1趟 //3.每一趟比
-
插入排序:从后向前,逐个比较数值大小,找到相应的位置插入(前小后大)
for (int i = 1; i < arr.length ; i++) { for (int j = 0; j < i; j++) { if (arr[i] < arr[j]) { int temp = 0; temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } System.out.println("排序后: " + Arrays.toString(arr)); } //1.从第一个元素开始(不是第0个) //2.只要前一个元素大于当前元素,该元素就向前移动 //3.直到前一个元素等于或小于当前元素(停止之后的比较),并放置在这后面
6.1排序方法的效率
快速排序 > 冒泡排序 > 选择排序 > 插入排序
// 返回1970年到当前一共经历了多少个毫秒
long start = System.currentTimeMillis();
// bubbleSort(arr); // 127
// selectSort(arr); // 39
// inertSort(arr); // 42
quickSort(arr); // 4
// 返回1970年到当前一共经历了多少个毫秒
long end = System.currentTimeMillis();
7.foreach遍历数组
foreach的设计使用来简化遍历的
//格式
for(元素数据类型 元素名 : 数组/集合) {
System.out.println(元素变量名)
}
7.1普通for和foreach的区别
- foreach更简洁
- foreach没有索引,普通for有索引
- foreach的底层是普通for,只不过定义了一个临时数组过渡
8.可变参数
用于多个参数数据类型一致,但数量不同的方法
特点:
- 可变参数的本质还是数组,参数数据类型一致,数量不同
- 可变参数的 " … " 位置在数据类型和变量名之间(eg:int … a)
- 可变参数必须出现在参数列表的最后,因为要是在前面,会吸收所有的值,后面的参数无法赋值
9.Arrays工具类
针对数组操作的工具类,方便操作数组
9.1Arrays.copyOf & System.arraycopy
实现对数组的任意位置的任意元素进行增加和删除
优点:查询和修改效率非常高 【索引】
缺点:数组作为容器增加和删除的效率低(因为数组声明赋值的时候就确定了长度,所以增删实际上都是创建了新的数组)
本质:是一种(数据结构:栈 队列 堆 哈希表 链表 二叉树…)
9.2工具类的部分功能
-
遍历
// 遍历 String s = Arrays.toString(arr); System.out.println(s);
-
快速排序
// 排序 Arrays.sort(arr); System.out.println("排序后: " + Arrays.toString(arr));
-
二分法查找
// 二分法查找 System.out.println("排序后: " + Arrays.binarySearch(arr, 22));
-
数组拷贝(Arrays.copyOf)
// 数组的拷贝 // 第一个参数表示需要拷贝的源数组 int[] newArr = Arrays.copyOf(arr, arr.length); System.out.println("拷贝后的数组: " + Arrays.toString(newArr)); //最末尾增加 int[] addArr = Arrays.copyOf(arr, arr.length + 1); System.out.println("拷贝后的数组: " + Arrays.toString(addArr)); //在末尾减少 int[] removeArr = Arrays.copyOf(arr, arr.length - 1); System.out.println("拷贝后的数组: " + Arrays.toString(removeArr));
-
数组拷贝(System.arraycopy)
/* * 数组拷贝 * src: 需要拷贝的源数组 * srcPos: 源数组的拷贝的起始索引 * dest: 需要拷贝的目标数组 * destPos: 需要拷贝的目标数组的起始索引 * length: 从源数组中拷贝多少个长度到目标数组 */ int[] srcArr = {11, 22, 33, 99, 55, 88}; int[] destArr = new int[srcArr.length]; System.out.println(Arrays.toString(destArr)); System.arraycopy(srcArr, 0, destArr, 0, srcArr.length); System.out.println(Arrays.toString(destArr));
-
填充数组
// 填充数组 Arrays.fill(arr, 1); System.out.println(Arrays.toString(arr));
10.二维数组
存放一维数组的数组(数组可以存放引用数据类型,数组是引用数据类型)
10.1动态初始化
-
//格式一: 数据类型[][] 数组名 = new 数据类型[m][n]; //变式: 数据类型 数组名[][] = new 数据类型[m][n]; 数据类型[] 数组名[] = new 数据类型[m][n];
-
//格式二: 数据类型[] 数组名[] = new 数据类型[m][];
注意:
- m是必不可少的,n可以省略
- m表示的是二维数组中,一维数组的数量
- n表示的是一维数组中,元素的数量
//n省略的情形
int[][] arr = new int[3][];
System.out.println(arr); // [[I@7852e922
System.out.println(arr[0]); // null
System.out.println(arr[1]); // null
System.out.println(arr[2]); // null
System.out.println(arr[0][0]); // java.lang.NullPointerException(空指针异常)
//一维数组没有new去开辟堆区的空间,没有地址值
//所以是默认值:null
//直接arr[0][0]会出现空指针异常的报错
//需要对每一个一维数组进行new
//解决方式:
arr[0] = new int[3];
arr[1] = new int[1];
arr[2] = new int[] {11,22,33,44};
System.out.println(arr[0]); // [I@4e25154f
System.out.println(arr[1]); // [I@70dea4e
System.out.println(arr[2]); // [I@5c647e05
System.out.println(arr[0][0]); // 0
System.out.println(arr[0][1]); // 0
10.2静态初始化
//格式一:
数据类型[][] 数组名 = {{元素1,元素2}, {元素3,元素4,元素5}, {元素6}};
//格式二:
数据类型[][] 数组名 = new 数据类型[][]{{元素1,元素2}, {元素3,元素4,元素5}, {元素6}};
10.3二维数组的遍历
二维数组是不能通过Arrays.toString(数组名)来实现遍历的
//Arrays.toString(arr)遍历的是一维数组的地址值
int[][] arr = new int[][]{{11,22,33},{44,55,66,77},{33,22}};
System.out.println(Arrays.toString(arr));
//[[I@15db9742, [I@6d06d69c, [I@7852e922]
//二维数组的遍历方法
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.println(arr[i][j]);
}
}
10.4一二维数组地址的区别
- 一维数组:[I@+(Number…) ( eg: [I@7852e922
- 二维数组:[[I@+(Number…) ( eg: [[I@7852e922
*10.5二维数组的面试题
int[] x,y[];
//int[] x ;int[]y[];
int n[] = new int[2],i,j,k;
//int n[] = new int[2];
//int i;
//int j;
//int k;
//“,”表示的是分别声明赋值 (eg:for(int i = 0,y = 0; ; ) { }