数组的理解:数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理
数组的相关概念
数组的特点:
数组是有序排列的
数组属于引用数据类型。数组的元素既可以是基本数据类型,也可以是引用数据类型(如String)
创建数组对象会在内存中开辟一整块连续的内存空间
数组的长度一旦确定,就不能修改。
数组的分类:
一维数组的使用
①一维数组的声明和初始化
②如何调用数组的指定位置的元素
③如何获取数组的长度
④如何遍历数组
⑤数组元素的默认初始化
⑥数组的内存解析
//1.一维数组的声明和初始化 int[] ids;//声明 //1.1静态初始化:数组的初始化和数组元素的赋值操作同时进行 ids = new int[]{1001,1002,1003,1004}; //1.2动态初始化:数组的初始化和数组元素的赋值操作分开进行 String[] names = new String[5];//只需声明数组的长度即可,切不可同时声明元素 //总结:数组一旦初始化完成,其长度就确定了 //2.如何调用数组的指定位置的元素:通过下标的方式调用 //数组下标从0开始,到数组长度-1结束。 names[0] = "赵"; names[1] = "钱"; names[2] = "孙"; names[3] = "李"; names[4] = "刘"; //3.如何获取数组的长度 //属性:length System.out.println(names.length);//5 //4.遍历数组(利用循环) for(int i = 0;i < names.length;i++){ System.out.println(names[i]); } //5.数组元素的默认初始化值 /* 基本数据类型时 >数组元素是整型:0 >数组元素是浮点型:0.0 >数组元素是char型:0或'\u0000',而非'0' >数组元素是bool型:false 引用数据类型时:null */
内存的简化结构
以后只要见到new一个对象,那么堆空间中就重新开辟
一维数组内存解析图解
真实的字符串等引用数据类型并不是放在数组中的,而是放在方法区中的常量池,数组中存放的是指向它的指针,现在不用了解太多,后续面向对象还会讲。(引用数据类型要么存放地址值,要么就是null)
一维数组练习题
public class Project { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入学生的人数"); int stuNum = scanner.nextInt(); int[] scores = new int[stuNum]; System.out.println("请输入学生的成绩"); int max = 0; for (int i = 0; i < scores.length; i++) { int grades = scanner.nextInt(); scores[i] = grades; //找出最大成绩 if (max < scores[i]){ max = scores[i]; } } System.out.println("最高分是" + max); for (int i = 0; i < scores.length; i++) { if(max - scores[i] <= 10){ System.out.println("student " + i +" score is " + scores[i] + " grade is " + 'A'); }else if (max - scores[i] <= 20){ System.out.println("student " + i +" score is " + scores[i] + " grade is " + 'B'); }else if (max - scores[i] <= 30){ System.out.println("student " + i +" score is " + scores[i] + " grade is " + 'C'); }else { System.out.println("student " + i +" score is " + scores[i] + " grade is " + 'D'); } } } }
二维数组的使用
理解:可以把二维数组看成是一维数组array1又作为另一个一维数组array2的元素而存在。其实,从数组底层的运行机制来看,并没有多维数组。
二维数组的使用
①二维数组的声明和初始化
②如何调用数组的指定位置的元素
③如何获取数组的长度
④如何遍历数组
⑤数组元素的默认初始化
⑥数组的内存解析
public class ArrayTest { public static void main(String[] args) { int[] arr1 = new int[]{1, 2, 3}; //如下方式也是正确的,不标准 int arr7[] = new int[]{1,2,3}; //省略也正确,不标准 int[] arr8 = {1,2,3};//类型推断 //1.二维数组的声明和初始化 //静态初始化 int[][] arr2 = new int[][]{{1,2,3},{4,5},{6,7,8}}; //动态初始化1 int[][] arr3 = new int[3][2]; //动态初始化2 int[][] arr4 = new int[3][]; //如下定义也正确,不常用,不标准 int arr5[][] = new int[3][]; int[] arr6[] = new int[3][]; //省略也正确,不标准 int[][] arr9 = {{1,2,3},{4,5},{6,7,8}};//类型推断 //2.如何调用数组指定位置的元素 System.out.println(arr2[0][1]);//2 System.out.println(arr3[1][1]);//0 //arr4还没有初始化完,若直接调指定位置元素会报错,所以要先初始化完 //从二维数组的结构去理解,一个数组去充当另一个数组的元素 arr4[1] = new int[4]; System.out.println(arr4[1][2]);//0 //3.获取二维数组的长度 System.out.println(arr2.length);//3 System.out.println(arr2[0].length);//3 System.out.println(arr2[1].length);//2 //4.遍历二维数组,用到两层for循环 for (int i = 0; i < arr2.length; i++) { for (int j = 0; j < arr2[i].length; j++) { System.out.print(arr2[i][j] + " "); } System.out.println(); } //5.数组元素的默认初始化值 /* 规定:二维数组分为外层数组元素,内层数组元素 int[][] arr = new int[4][3]; 外层元素:arr[0],arr[1]等 内层元素:arr[0][0],arr[1][2]等 */ System.out.println(arr3[0]);//[I@1b6d3586,是一个内存地址,I代表int类型 System.out.println(arr3[0][0]);//0 System.out.println(arr3);//[[I@4554617c,同样也是一个地址值 String[][] arr10 = new String[4][3]; System.out.println(arr10[1]);//地址值 System.out.println(arr10[1][1]);//null double[][] arr11 = new double[4][]; System.out.println(arr11[1]);//null,因为二维数组可以看成一个一维数组放到另一个一维数组中,数组是引用数据类型,默认值都是null System.out.println(arr11[1][1]);//报错,会报空指针异常,因为指向下一个数组的指针不存在 } }
图解
arr是一个地址值
二维数组的内存解析
其中1,4在面试中考察的比较多一些
以下操作是赋值,不是数组的复制
public static void main(String[] args) { int[] array1,array2; array1 = new int[]{2,3,5,7,11,13,17,19}; for (int i = 0; i < array1.length; i++) { System.out.print(array1[i] + " "); } //赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值(如array[2] = 2),打印出array1 array2 = array1;//这个操作只是将array1的地址值给了array2 for (int i = 0; i < array2.length; i++) { if (i % 2 == 0){ array2[i] = i; } } System.out.println(); for (int i = 0; i < array1.length; i++) { System.out.print(array1[i] + " "); } }
图解如上代码,只要是new一个,就会在堆中新开辟一个空间,此段代码只new了一个,因此堆中只有一个
真正的复制
//将array1的值复制给array2 array2 = new int[array1.length]; for (int i = 0; i < array1.length; i++) { array2[i] = array1[i]; }
图解复制,中间new了两个
数组的反转,以后进行反转操作都可以用以下两种方法
public static void main(String[] args) { String[] arr = new String[]{"赵","钱","孙","李"}; //数组的反转 //方式一 for (int i = 0; i < arr.length / 2; i++) { String temp = arr[i]; arr[i] = arr[arr.length - i - 1]; arr[arr.length - i - 1] = temp; } //方式二 for (int i = 0,j = arr.length - 1; i < j; i++,j--) { String temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } //遍历 for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } }
查找(线性查找)
String[] arr = new String[]{"赵","钱","孙","李"}; //查找(线性查找,从前往后查) String dest = "孙"; dest = "刘"; //设置标志 boolean flag = true; for (int i = 0; i < arr.length; i++) { if (dest.equals(arr[i])){ System.out.println("找到了" + dest + "的位置在" + i); flag = false; break; } } if (flag){ System.out.println("很抱歉没有找到!"); } /* 或者改为 if(i == arr.length){ System.out.println("很抱歉没有找到!"); } 循环到i = arr.length - 1;i++,此时i = arr.length,不满足循环条件,则跳出循环 */
二分查找(前提是数组有序),先熟悉,开发中有专门方法
//二分法查找 int[] arr = new int[]{-10,-6,0,2,5,8}; Scanner scanner = new Scanner(System.in); while (true){ System.out.println("请输入要查找的数字"); int num = scanner.nextInt(); if (num == -1){ System.out.println("程序运行结束!"); break; } int head = 0;//初始化头部索引 int end = arr.length - 1;//初始化尾部索引 //定义标志位 boolean flag = true; while (head <= end){ int mid = (head + end) / 2; if (num == arr[mid]){ System.out.println("找到了指定元素位置" + mid); flag = false; break; }else if (arr[mid] < num){ head = mid + 1; }else { end = mid - 1; } } if (flag){ System.out.println("抱歉没有找到"); } }
排序
选择排序:每一趟在待排序元素中选取关键字最小(或最大)的元素加入有序子序列
插入排序:基本思想:每次将一个待排序的元素,按其关键字的大小插入到前面已经排好序的子文件的适当位置,直到全部记录插入完成为止。
冒泡排序(属于交换排序,每次比较相邻元素)
//冒泡排序(升序) int[] arr = new int[]{-1,-3,-9,0,9,3,8}; //轮数,需要length - 1 轮 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 = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); }
快速排序(使用最多效果最好的),目前知道过程即可
private static void swap(int[] data, int i, int j) { int temp = data[i]; data[i] = data[j]; data[j] = temp; } private static void subSort(int[] data, int start, int end) { if (start < end) { int base = data[start]; int low = start; int high = end + 1; while (true) { while (low < end && data[++low] - base <= 0) ; while (high > start && data[--high] - base >= 0) ; if (low < high) { swap(data, low, high); } else { break; } } swap(data, start, high); subSort(data, start, high - 1);//递归调用 subSort(data, high + 1, end); } } public static void quickSort(int[] data){ subSort(data,0,data.length-1); } public static void main(String[] args) { int[] data = { 9, -16, 30, 23, -30, -49, 25, 21, 30 }; System.out.println("排序之前:\n" + java.util.Arrays.toString(data)); quickSort(data); System.out.println("排序之后:\n" + java.util.Arrays.toString(data)); }
快速排序图解
算法性能对比:背下冒泡平均复杂度O(n2),快速排序平均时间复杂的O(nlogn)
java中封装了很多工具类可以加快开发速度,需要用到的时候直接调用即可(不知道的可以查看API文档)
public static void main(String[] args) { //判断两个数组是否相等 int[] arr1 = new int[]{1,2,3,4}; int[] arr2 = new int[]{1,3,2,4}; boolean isEquals = Arrays.equals(arr1,arr2); System.out.println(isEquals); //输出数组信息 System.out.println(Arrays.toString(arr1)); //将指定值填充到数组中 Arrays.fill(arr2,10); System.out.println(Arrays.toString(arr2));//[10,10,10,10] //对数组进行排序 int[] arr3 = new int[]{-10,-3,3,42,2}; Arrays.sort(arr3); System.out.println(Arrays.toString(arr3)); //对排序后的数组进行二分查找 int index = Arrays.binarySearch(arr3,3); System.out.println(index); }
上下界都不能超过
//情况一: int[] arr5 = new int[]{1,2,3}; arr5 = null; System.out.println(arr5[0]); //情况二 int[][] arr6 = new int[4][]; System.out.println(arr6[0][0]); //情况三 String[] arr7 = new String[]{"a","b","c"}; arr7[0] = null; System.out.println(arr7[0].toString());