【Java】08 Array 数组
概述
数组是多个相同数据类型按一定顺序排列的一组数据
特点:
- 数据类型相同!!
- 长度固定!!
构成数组的几个要素
- 数组名称
- 下标,又称索引
- 元素
- 数组长度
数组是一种引用类型,就像使用变量一样必须要有引用才能可控的访问
下标是数组的书签,访问数组的元素必须依靠下标
元素,数组所存储的数据,元素的数据类型必须和数组相同
长度,数组所存储的元素的个数
按维度分类,可以分为一维数组和多维数组
声明与初始化
package cn.dai; public class Arrays { public static void main(String[] args) { //声明一个数组 int[] array; // 初始化 array = new int[]{1,2,3,4,5,6,7}; // 如果简写,也就是静态初始化,必须 声明和初始化同时完成 int[] array2 = {2,3,4,5,7,8,9}; // 或者不对数组具体赋值元素而是初始化时声明数组的长度 int[] array3 = new int[10]; } }
访问元素和使用下标
package cn.dai; public class Arrays { public static void main(String[] args) { int[] array = {1,3,5,7,9}; // 下标起始位置从0开始,结尾到数组的长度-1 System.out.println(array[0]); // 通过下标更改元素的值 array[3] = 10; } }
长度与遍历
package cn.dai; public class Arrays { public static void main(String[] args) { int[] array = {1,3,5,7,9}; // 使用for循环遍历数组 length是数组的一个属性,表示数组的长度 for (int i = 0; i < array.length ; i++) { System.out.println( array[i] ); } } }
默认值?引用类型的属性问题
一维数组的内存分布图
多维数组
声明和初始化
package cn.dai; public class Arrays { public static void main(String[] args) { // 二维数组的初始化 静态 int[][] array = new int[][]{ {1,3,5,7,9}, {2,4,6,8,10} }; // 动态 二维数组可以只声明外围长度, // 内围数组没有声明就是未初始化的状态 // Java允许内围可以先不初始化 int[][] array2 = new int[4][]; } }
下标访问和遍历
package cn.dai; public class Arrays { public static void main(String[] args) { // 二维数组的初始化 静态 int[][] array = new int[][]{ {1,3,5,7,9}, {2,4,6,8,10} }; // 元素的访问 System.out.println(array[1][2]); // 遍历 // 先遍历外围长度,也就是内围的数组个数 for (int i = 0; i < array.length; i++) { // 再遍历下标指向的数组 for (int j = 0; j < array[i].length ; j++) { // 这样就能遍历每一个元素了 System.out.println( array[i][j] ); } } } }
数组练习
杨辉三角/帕斯卡三角
package cn.dai; public class Arrays { public static void main(String[] args) { // 杨辉三角形 外围的长度不限制 (不要低于5) int[][] pascalTriangle = new int[10][]; // 名字太长 声明一个简写引用一下 int[][] tri = pascalTriangle; for (int i = 0; i < tri.length; i++) { // 声明内围的数组 tri[i] = new int[i + 1]; // 连续赋值 每一行,也就是每一组数组的头和尾都是1 tri[i][0] = tri[i][i] = 1; // 对既不是头也不是尾的内部元素进行赋值 for (int j = 1; j < tri[i].length - 1 ; j++) { tri[i][j] = tri[i - 1][j - 1] + tri[i - 1][j]; } } // 打印查看 for (int i = 0; i < tri.length; i++) { for (int j = 0; j < tri[i].length; j++) { System.out.print(tri[i][j]+"\t"); } System.out.println(); } } }
一维数组:求最大最小值,平均值,总和
package cn.dai; public class Arrays { // 求最大值 static int getMaximum(int[] array){ // 设置游标 int max = array[0]; // 因为已经设置第一个赋值给了游标 从第二个开始遍历 for (int i = 1; i < array.length; i++) { // max = array[i] > max ? array[i] : max; max = Math.max(array[i], max); } return max; } // 求最小值 static int getMinimum(int[] array){ int min = array[0]; for (int i = 1; i < array.length; i++) { // min = array[i] < min ? array[i] : min; min = Math.min(array[i], min); } return min; } // 求和 static int getSummation(int[] array){ int sum = 0; for (int element : array) { sum += element; } return sum; } // 求平均值 static double getAverage(int[] array){ double sum = 0.0; for (int element : array) { sum += element; } return sum / array.length; } }
一维数组的复制
package cn.dai; public class Arrays { public static void main(String[] args) { int[] array = {1,3,5,7,9}; // 先复制长度 int[] array2 = new int[array.length]; for (int i = 0; i < array2.length; i++) { // 复制元素 array2[i] = array[i]; } } }
一维数组的反转
package cn.dai; public class Arrays { public static void main(String[] args) { int[] array = {1,2,5,10,9}; } static int[] reverse1(int[] array){ // 遍历一半长度进行元素交换 for (int i = 0; i < array.length / 2; i++) { int temp = array[i]; array[i] = array[array.length - 1 - i]; array[array.length - 1 - i] = temp; } return array; } static int[] reverse2(int[] array){ // 头尾双向交换 for ( int i = 0,j = array.length -1; i < j ; i++,j--) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } }
一维数组的线性查找 和 二分查找
package cn.dai; public class Arrays { public static void main(String[] args) { int[] array = {1,2,5,10,9}; } // 线性查找 static int linearSearch(int[] array,int target){ for (int i = 0; i < array.length; i++) { if (target == array[i]) return i; // 找到直接返回索引 } return -1; // 找不到 返回-1 } // 二分查找 static int binarySearch(int[] array,int target){ int start = 0; int end = array.length -1; while ( start <= end ){ int mid = (start + end) / 2; // 每次二分都在不断变化的中轴游标 if (target == array[mid]) return mid; // 碰上了正好直接返回 else if (target > array[mid]) start = mid + 1; // 目标位置大于中轴,起始位置前推 else end = mid + 1; // 反之后退 } return -1; } }
【排序算法】
衡量排序算法的指标
- 时间复杂度 需要比较的次数和记录的移动次数
- 空间复杂度 内存耗费
- 稳定性 是否需要改变数据
按内外状态划分
- 内部排序 不需要外界辅助实现算法
- 外部排序 算法需要多部份完成,依赖外界辅助实现
【算法的特征】
- 对输入的描述和定义必须正确
- 输出必须要有结果产生
- 有穷性,在完成计算实现之后必须停止
- 确定性,算法的每一步都是明确的,不会歧义
- 可行性,算法的每一步都是清晰的,纸笔也能实现答案获取
冒泡排序
package cn.dai; public class Arrays { public static void main(String[] args) { int[] array = {1,2,5,10,9}; } // 冒泡排序 static int[] bubbleSort(int[] array){ for (int i = 0; i < array.length -1; i++) { for (int j = 0; j < array.length -1; j++) { if (array[j] > array[j + i]){ int temp = array[i]; array[j] = array[j + 1]; array[j + 1] = temp; } } } return array; } }
快速排序 递归
https://blog.csdn.net/shujuelin/article/details/82423852
package cn.dai; public class Arrays { public static void main(String[] args) { int[] array = {1,2,5,10,9}; } // 快速排序 static void quickSort(int[] arr,int start,int end){ int i,j,temp,t; if(start > end) return; i = start; j = end; //temp就是基准位 temp = arr[start]; while (i < j) { //先看右边,依次往左递减 while (temp <= arr[j] && i<j) j--; //再看左边,依次往右递增 while (temp>=arr[i]&&i<j) i++; //如果满足条件则交换 if (i<j) { t = arr[j]; arr[j] = arr[i]; arr[i] = t; } } //最后将基准为与i和j相等位置的数字交换 arr[start] = arr[i]; arr[i] = temp; //递归调用左半数组 quickSort(arr, start, j-1); //递归调用右半数组 quickSort(arr, j+1, end); } }
Arrays工具类
直接看源码,这里有非常多的重载
右边可以看到所有基本类型的重载方法
Arrays.sort() 将参数数组按顺序方式排序
关于parallelSort并行排序的资料:
https://www.infoq.cn/article/Java-8-Quiet-Features/
二分的重载
判断两个数组是否相同
fill 填充数组 你可以设置填充范围,不过默认全填充
复制数组直接照搬系统的复制方法...
将数组转换成String字符串形式
越界异常
package cn.dai; public class Arrays { public static void main(String[] args) { int[] array = {1,2,5,10,9}; // 越界异常 访问超过索引个数的位置 System.out.println(array[11]); } } Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 11 at cn.dai.Arrays.main(Arrays.java:10) Process finished with exit code 1
数组比较,判断是否相等
比较的依据分两种,判断地址是否一致,按存储的元素和顺序是否一致:
int[] arrayA = {1, 3, 5}; int[] arrayB = arrayA; int[] arrayC = {1, 3, 5}; System.out.println("arrayA == arrayC : " + (arrayA == arrayC)); System.out.println("arrayA == arrayB : " + (arrayA == arrayB)); System.out.println("arrayA.equals(arrayB) : " + (arrayA.equals(arrayB))); System.out.println("arrayA.equals(arrayC) : " + (arrayA.equals(arrayC))); System.out.println("Arrays.equals(arrayA, arrayB) : " + Arrays.equals(arrayA, arrayB)); System.out.println("Arrays.equals(arrayA, arrayC) : " + Arrays.equals(arrayA, arrayC)); // 存在多维数组的情况 int[][] arrayD = { {1, 3}, {3, 4}, {5, 6} }; int[][] arrayE = { {1, 3}, {3, 4}, {5, 6} }; System.out.println("arrayD == arrayE : " + (arrayD == arrayE)); System.out.println("Arrays.equals(arrayD, arrayE) : " + Arrays.equals(arrayD, arrayE)); System.out.println("Arrays.deepEquals(arrayD, arrayE) : " + Arrays.deepEquals(arrayD, arrayE)); System.out.println("Arrays.deepEquals(null, null) : " + Arrays.deepEquals(null, null));
查看打印结果:
arrayA == arrayC : false arrayA == arrayB : true arrayA.equals(arrayB) : true arrayA.equals(arrayC) : false Arrays.equals(arrayA, arrayB) : true Arrays.equals(arrayA, arrayC) : true arrayD == arrayE : false Arrays.equals(arrayD, arrayE) : false Arrays.deepEquals(arrayD, arrayE) : true Arrays.deepEquals(null, null) : true Process finished with exit code 0
我们知道 双等号是取引用存放的地址进行比较
但是发现数组对象自带的equals方法也返回false
查看方法所属的类是Object,可以得知数组的equals方法是没有重写处理的
就是按照Object类默认的方式进行比较:
public boolean equals(Object obj) { return (this == obj); }
所以下面两种办法是等同的:
arrayA == arrayC : false arrayA.equals(arrayC) : false
再来看Arrays工具类提供的比较办法:
1、优先看地址,一样的就不用看其它条件,直接确认一致
2、如果其中一个为空就返回false
3、看长度是否一致,长度都不一样就确认不是了
4、然后遍历元素,取元素来比较是否一直
public static boolean equals(int[] a, int[] a2) { if (a==a2) return true; if (a==null || a2==null) return false; int length = a.length; if (a2.length != length) return false; for (int i=0; i<length; i++) if (a[i] != a2[i]) return false; return true; }
多维数组的比较使用的是Arrays工具类的deepEquals()方法
public static boolean deepEquals(Object[] a1, Object[] a2) { if (a1 == a2) return true; if (a1 == null || a2==null) return false; int length = a1.length; if (a2.length != length) return false; for (int i = 0; i < length; i++) { Object e1 = a1[i]; Object e2 = a2[i]; if (e1 == e2) continue; if (e1 == null) return false; // Figure out whether the two elements are equal boolean eq = deepEquals0(e1, e2); if (!eq) return false; } return true; }
该方法要求入参对象是Object数组类型,可以尝试把一维基本类型数组放进去之后会编译报错
因为一维数组的对象定义为Object类型,所以类型是不匹配的
判断思路还是一致,常规的地址,空指针,长度不一致是先判断
在元素比较时单独封装了一个方法来编写
1、如果元素还是一个数组对象,会递归调用深度比较方法,也就是多维数组会被方法依次剥离引用来判断
2、直到剥离成最基本的一维数组时,开始调用其他基本类型一维数组方法去处理,方法得到了复用
static boolean deepEquals0(Object e1, Object e2) { assert e1 != null; boolean eq; if (e1 instanceof Object[] && e2 instanceof Object[]) eq = deepEquals ((Object[]) e1, (Object[]) e2); else if (e1 instanceof byte[] && e2 instanceof byte[]) eq = equals((byte[]) e1, (byte[]) e2); else if (e1 instanceof short[] && e2 instanceof short[]) eq = equals((short[]) e1, (short[]) e2); else if (e1 instanceof int[] && e2 instanceof int[]) eq = equals((int[]) e1, (int[]) e2); else if (e1 instanceof long[] && e2 instanceof long[]) eq = equals((long[]) e1, (long[]) e2); else if (e1 instanceof char[] && e2 instanceof char[]) eq = equals((char[]) e1, (char[]) e2); else if (e1 instanceof float[] && e2 instanceof float[]) eq = equals((float[]) e1, (float[]) e2); else if (e1 instanceof double[] && e2 instanceof double[]) eq = equals((double[]) e1, (double[]) e2); else if (e1 instanceof boolean[] && e2 instanceof boolean[]) eq = equals((boolean[]) e1, (boolean[]) e2); else eq = e1.equals(e2); return eq; }
将数组转换成字符串的优雅方法:
int[][] arrayD = { {1, 3}, {3, 4}, {5, 6} }; System.out.println("Arrays.toString(arrayD) -> " + Arrays.toString(arrayD)); System.out.println("Arrays.deepToString(arrayD) -> " + Arrays.deepToString(arrayD));
打印结果:
Arrays.toString(arrayD) -> [[I@7f31245a, [I@6d6f6e28, [I@135fbaa4] Arrays.toString(arrayD) -> [[1, 3], [3, 4], [5, 6]]
分别适用在一维数组和多维数组中