数组的概念
概念
数组: 是多个相同类型数据按照一定排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理
数组中的概念
数组名: 数组的名称,命名
下标: 从0开始
元素:数组中存储的数据
数组的长度: 数组的存储了多少个元素
特点
- 数组中的元素在内存中是依次紧密排列的,有序的
- 数组属于引用数据类型的变量,但是数组的元素 - 既可以是基本数据类型,也可以是引用数据类型
- 数组一旦初始化了,数组的长度就固定了,不能再更改了(声明了就申请了内存大小)
- 创建数组会在内存中开辟一整块连续空间,占据空间的大小,取决于数组的长度和数组中元素的类型
分类
按照元素类型:基本数据类型元素数组;引用数据类型数组
按照数组的维度:一维数组;二维数组;....
一维数组
数组的定义与初始化
public class Main {public static void main(String[] args) {// 声明1int[] intArray;// 赋值intArray = new int[10];// 声明2double[] doubleArray;// 赋值2doubleArray = new double[]{1.0, 2.0, 3.0};// 声明+赋值一起初始化// 动态初始化: 数组变量的赋值和数组元素的赋值分开进行int[] arrayInt = new int[10];arrayInt[0] = 1;// 静态初始化:数组变量的赋值和数组的元素赋值同时进行String[] stringArray = new String[]{"测试1", "测试2"};Double[] doubleArrayDouble = {1.0, 2.0, 3.0};// 数组的取值System.out.println(doubleArrayDouble[0]);System.out.println(doubleArrayDouble[1]);System.out.println(doubleArrayDouble[2]);System.out.println(doubleArrayDouble[3]); // 超出数组长度,报错:ArrayIndexOutOfBoundsException// 数组的length与遍历for(int i = 0; i < doubleArrayDouble.length; i++) {System.out.println(doubleArrayDouble[i]);}}
}
不同类型数组的初始值
// 数组默认值// 整型数组元素的默认初始化值int[] intArrary1 = new int[10];System.out.println(intArrary1[0]); // 0// 浮点型数组元素的默认初始化值double[] doubleArrayDouble1 = new double[10];System.out.println(doubleArrayDouble1[0]); // 0.0// 字符型数组元素默认初始值char[] charArray = new char[10];System.out.println(charArray[0]); // 数字0if(charArray[0] == 0) {System.out.println("是数字0");}if(charArray[0] == '0') {System.out.println("是字符0");}boolean[] booleanArray = new boolean[10];System.out.println(booleanArray[0]); // falseString[] stingArray = new String[10];System.out.println(stingArray[0]); // null
java虚拟机的内存划分
为了提高运算效率,对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方法
虚拟机栈: 用于存储正在执行的每个java方法的局部变量表等,局部变量存放了编译期可知长度的各种数据类型,对象引用,方法执行完,自动释放。
堆内存: 存储对象(包括数组对象),new来创建的,都存储在堆内存
方法区: 存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据
本地方法栈:当程序中调用了native的本地方法时,本地方法执行期间的内存区域
程序计数器:程序计数器是CPU中的寄存器,他包含每个线程下一条要执行的指令的地址
存储结构
和前端相似,也是栈和堆,栈中存储的名和地址值,地址值指向堆
虚拟机栈: 存放的数组名称
堆: 存放的数组的实体
案例一
案例二
综上可以看出,和前端存储和修改是一致的,如案例二,修改test2,test1的值会同步修改;
同理: 数组的比较,比较的是地址值 intTest == intTest2 ture;
二维数组
二维数组: 可以看成一维数组又作为另外一个一堆数组array2的元素存在,其实从数组的底层运行机制来看,是没有多维数组的
数组的定义与初始化
class TwoDimensionarArray {public static void main(String[] args) {// 静态初始化:赋值和元素赋值同时进行int[][] intTest = new int[][]{{1,2,3}, {2,3,4}, {3,4,5}};// 动态初始化String[][] stringTest = new String[3][3];String[][] stringTest2 = new String[3][];// 其他正确方式int arrAry[][] = new int[][]{{1,2,3}, {2,3,4}};int[] arrAry2[] = new int[][]{{1,2,3}, {2,3,4}};int arrARry3[][] = {{1,2,3}, {3,4,5}};// 取值System.out.println(arrAry[0][0]); //1System.out.println(arrAry.length); //3for(int i = 0; i < arrAry.length; i++) {for(int j = 0; j < arrAry[i].length; j++) {System.out.println((arrAry[i][j]));}}}
}
不同类型数组的初始值
- 不同类型的值如果定义是外层是地址,内层和一维数组的初始值相同
int[][] arr1 = new int[3][2];System.out.println(arr1[0]); // 地址值System.out.println((arr1[0][0])); // 0
- 如果是初始化定义不同,如下,外层为空,内层会报错空指针
int[][] arr2 = new int[4][];System.out.println(arr2[0]); // null 由于二维数组没有定义值,导致栈中没有存储地址值System.out.println(arr2[0][0]); // 报错空指针,没有地址值再去取值会报空指针
存储结构
数组中常见算法
案例一
// 定义一个int类型一维数组,包含10个元素,分别赋值一些随机整数,然后求出元素最大值,最小值,总和,并输出
// 所有的随机整数都是两位数[10,99]
class TestCase1 {public static void main(String[] args) {int[] nums = new int[10];int len = nums.length;for(int i = 0; i < len; i++) {nums[i] = (int)(Math.random()*90)+10;}int maxNum = nums[0];int minNum = nums[0];int sum = 0;int average = 0;for(int i = 0; i < len; i++) {maxNum = nums[i] > maxNum ? nums[i] : maxNum;minNum = nums[i] < minNum ? nums[i] : minNum;sum += nums[i];}average = sum/len;System.out.println(maxNum);System.out.println(minNum);System.out.println(sum);System.out.println(average);}
}
案例二
// 评委打分
// 编程竞赛中,10位评委打分 - 5.4.6.8.9.0.1.2.7.3
// 求最后得分 - (去掉一个最高分,去掉一个最低分后其余8位评委的平均分)
class TestCase2{public static void main(String[] args) {int[] scores = {5,4,6,8,9,0,1,2,7,3};int maxNum = scores[0];int minNum = scores[0];double sumNum = 0.0;for(int i = 0; i < scores.length; i++) {maxNum = scores[i] > maxNum ? scores[i] : maxNum;minNum = scores[i] < minNum ? scores[i] : minNum;sumNum += scores[i];}sumNum = sumNum - (maxNum + minNum);System.out.println(sumNum/8);}
}
案例三 - 杨辉三角
// 利用二维数组 - 实现10行杨辉三角数据
// 1. 第一行有1个元素,第n行有n个元素
// 3. 从第三行开始,对于非第一个元素和最后一个元素的元素,等于数值相加
class TestCase3 {public static void main(String[] args) {// 初始化定义数组长度int[][] arr = new int[10][];for(int i = 0; i < arr.length; i++) {// 定义每一行的长度 - 内层数组的个数arr[i] = new int[i+1];for(int j = 0; j < i+1; j++) {if(j == 0 || (j > 0 && j == arr[i].length - 1)) {// 每一行第一个元素和最后一个元素为1arr[i][j] = 1;}else {// 其余数为上一行对应数据和对应数据前一个数据相加arr[i][j] = arr[i-1][j-1] + arr[i-1][j];}}}// 打印二维数组for(int i = 0; i < arr.length; i++) {for(int j = 0; j < arr[i].length;j++) {System.out.print(arr[i][j] + "\t");}System.out.print("\n");}}
}
案例四
// 生成一个随机数 - 随机数为[1,30],要求加入数组且数组长度为6,但数组中的随机数均不相等
class TestCase4 {public static void main(String[] args) {int[] arr = new int[6];for(int i = 0; i < arr.length; i++) {// 获取随机数arr[i] = (int) (Math.random() * 30) + 1;for (int j = 0; j < i; j++) {if (arr[i] == arr[j]) {i--;break;}}}for(int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}
}
案例五- 数组的拷贝
与前端相似,数组也有拷贝,他们的原理是相似的,可以看一下代码
class ArrayTest {public static void main(String[] args) {int[] array1 = {2,3,5,6,11,13,17,19};int[] arrary2, array3;for (int i = 0; i <array1.length; i++) {System.out.println(array1[i]);}// 赋值操作 - 理解为前端的浅拷贝,只拷贝了地址值;// 1和2都指向了同一个地址值,导致修改1,2中的任何一个值,另一个都会被修改arrary2 = array1;arrary2[0] = 0;// 深拷贝,arrary3 的值和arrary1的数据相同,但地址值不同,修改互不影响array3 = new int[array1.length];for(int i = 0; i < array1.length; i++) {array3[i] = array1[1]}System.out.println(array1[0]);}
}
案例六 - 数组的反转
// 数组的翻转 - [35,54,3,2,65,7,34,5,76,34,5,76,34,67]
class TestCase45 {public static void main(String[] args) {int[] arr = {35,54,3,2,65,7,34,5,76,34,5,76,34,67};for(int i = 0; i < arr.length/2; i++) {int temp = arr[i];arr[i] = arr[arr.length - 1 -i];arr[arr.length - 1 -i] = temp;}int[] arr2 = {35,54,3,2,65,7,34,5,76,34,5,76,34,67};for(int i = 0, j = arr2.length - 1; i < j; i++,j--) {int temp = arr2[i];arr2[i] = arr2[j];arr2[j] = temp;}}
}
案例七- 数组的扩容与缩容
class TestCase6 {public static void main(String[] args) {// 数组的扩容// 定义一个数组{1,2,3,4,5}, 将数组扩容1倍,并将,10,20,30添加到数组int[] arr1 = {1,2,3,4,5};// int[] arr2 = new int[arr1.length * 2];int[] arr2 = new int[arr1.length << 1];for(int i = 0; i < arr2.length; i++) {arr2[i] = arr1[i];}arr2[arr1.length] = 10;arr2[arr1.length + 1] = 20;arr2[arr1.length + 2] = 30;// 数组的缩容// 数组{1,2,3,4,5} 删除数组中索引为4的元素;// 方法1 不新建数组,在原有数组操作 - 推荐int[] arr = {1,2,3,4,5};for(int i = 4; i < arr.length - 1; i++) {arr[i] = arr[i + 1];}// 最后一个元素设置为初始值arr[arr.length-1] = 0;// 方法2 新建数组int[] arr3 = new int[arr.length - 1];for(int i = 0; i < 4; i++) {arr3[i] = arr[i];}for(int i = 4; i < arr.length -1; i++) {arr3[i] = arr[i+1];}}
}
案例八 - 线性查找
// 线性查找
class LineFind {public static void main(String[] args) {int[] arr = new int[]{34,54,2,4,56,7,8,10};int target = 34;// 方式一定义flagboolean find = false;for(int i = 0; i < arr.length; i++) {if(target == arr[i]) {find = true;System.out.println("找到"+ target + "在索引" + i);}}if(!find) {System.out.println("没有找到");}// 方式二int i = 0;for(; i < arr.length; i++) {if(target == arr[i]) {System.out.println("找到"+ target + "在索引" + i);}}if(i == arr.length) {System.out.println("没有找到");}}
}
案例九-二分查找
// 二分查找 - 必须是有序的数组
class TestCase7 {public static void main(String[] args) {int[] arr = {1,3,5,7,9,13,15,16,17,18,36,46,78,98};int target = 5;int minIndex = 0;int maxIndex = arr.length - 1;boolean flag= false;// 数组索引小于最大索引while (minIndex <= maxIndex) {int mid = minIndex + ((maxIndex - minIndex)/2);if(target == arr[mid]) {flag = true;System.out.println("找到"+target+"索引位置为"+mid);break;}else if(target > arr[mid]) {minIndex++;}else {maxIndex--;}}if(!flag) {System.out.println("未找到");}}
}
线性查找
- 优点:算法简单
- 缺点:执行率低,时间复杂度0(n)
二分法查找
- 优点:执行效率高
- 缺点: 算法更难,且数组必须为顺序排序
排序算法
衡量排序算法的优势
时间复杂度: 分析关键字的比较次数和记录的移动次数
空间复杂度:分析算法中需要多少辅助内存
稳定性:若两个记录A和B的关键值相等,但排序后,AB的先后次序保持不变,则称这种排序算法最稳定
排序算法分类
内部排序: 整个排序不需要借助外部存储器(如磁盘),所有排序操作在内存中完成
外部排序:参与排序的数据非常多,数据量非常大,计算器无法在整改排序过程中放在内存中完成,必须借助于外部存储完成
常见的排序
冒泡排序
每次相临的数据进行排序
如: 第一个元素与第二个元素排序,大的往后换;第二个在于第三个排序,大的往后;第一个索引的元素比完一轮,在进行第二元素排序;
// 冒泡排序
class TestCase8 {public static void main(String[] args) {int[] arr = new int[] {34,54,3,2,65,7,34,5,75,43,67};for(int j = 0; j < arr.length - 1; j++) {for(int i = 0; i < arr.length - 1 - j;i++) {if(arr[i] > arr[i+1]) {int temp = arr[i];arr[i] = arr[i+1];arr[i+1] = temp;}}}}
}
快速排序
第一轮:
以索引为0的值开始进行
public class QuickSort {// 主方法,用于测试快速排序public static void main(String[] args) {int[] array = {10, 7, 8, 9, 1, 5};int n = array.length;QuickSort ob = new QuickSort();ob.sort(array, 0, n - 1);System.out.println("排序后的数组:");printArray(array);}// 快速排序的递归实现void sort(int[] array, int low, int high) {if (low < high) {// pi 是分区索引,array[pi] 已经在正确的位置int pi = partition(array, low, high);// 分别对左右子数组进行排序sort(array, low, pi - 1);sort(array, pi + 1, high);}}// 分区操作,返回分区索引int partition(int[] array, int low, int high) {int pivot = array[high]; // 选择最右边的元素作为基准int i = (low - 1); // i 是较小元素的索引for (int j = low; j < high; j++) {// 如果当前元素小于或等于基准if (array[j] <= pivot) {i++;// 交换 array[i] 和 array[j]int temp = array[i];array[i] = array[j];array[j] = temp;}}// 交换 array[i + 1] 和 array[high] (或基准)int temp = array[i + 1];array[i + 1] = array[high];array[high] = temp;return i + 1;}// 打印数组的方法static void printArray(int[] array) {int n = array.length;for (int i = 0; i < n; ++i)System.out.print(array[i] + " ");System.out.println();}
}
数组的工具类 - java.util.Arrays
java.util.Arrays类即为操作数组的工具类,包含了用来操作数组的各种方法
class testUtilArray {public static void main(String[] args) {int[] arr = new int[]{1,4,5,6,7};int[] arr2 = new int[]{1,8,4,7,5};// 对比arr和arr2的元素是否相等System.out.println(Arrays.equals(arr, arr2));// string,输出Arrays.toString(arr);// 将制定数值填充Arrays.fill(arr, 1);// sort排序Arrays.sort(arr2);//二分查找 - 保证是排序了int num = Arrays.binarySearch(arr, 1); // 负数为没有找到}
}
异常
角标:ArrayIndexOutOfBoundException
空指针异常:NullPointerException
class TestError {public static void main(String[] args) {// ArrayIndexOutOfBoundExceptionint[] arr = {1,2,3,4};System.out.println(arr[5]);//NullPointerExceptionint[] arr1 = new int[10];arr1 = null;System.out.println(arr1[0]);int[][] arr2 = new int[3][];System.out.println(arr2[0][1]);String[] arr3 = new String[4];System.out.println(arr3[0]);}
}