Java基础5
数组的概念
数组(Array)是多个相同类型数据按照一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
几个相关概念: ①数组名 ②数组的元素 ③数组的下标 ④数组的长度
特点:
- 数组中的元素在内存中是依次紧密排列的,有序的。
- 数组,属于引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型。
- 数组一旦初始化完成,其长度就确定了并且长度不可更改。
- 创建数组对象会在内存中开辟一块完整的连续空间,占据的空间的大小,取决于数组的长度和数组中元素的类型。
数组的分类:
- 按照元素的分类:基本数据类型元素的数组;引用数据类型元素的数组
- 按照数组的维数来分:一维数组、二维数组......
一维数组
初始化: 静态初始化 动态初始化
//方式一 静态初始化:数组变量的赋值和数组元素的赋值操作同时进行 double[] prices; prices = new double[]{23.45,35.73,31.74}; //方式二 动态初始化:数组变量的赋值和数组元素的赋值操作分开进行 String[] week = new String[7]; //其他的正确方式 int arr[] = new int[3]; int[] arr1 = {1,3,6,7}; //类型推断
数组的调用: 角标的范围从0开始,到数组的长度-1结束
为什么数组的编号从0开始? 数组的索引,表示了数组元素距离首地址的偏移量。因为第1个元素的地址与首地址相同,所以偏移量是0.
数组的长度:用来描述数组容器中容量的大小 XXX.length
注意: 数组没有length()方法,有的是length属性; 但是String有length()方法。
//初始化 double[] prices; prices = new double[]{23.45,35.73,31.74}; //数组长度 System.out.println(prices.length); //遍历数组: for (int i = 0; i <= prices.length; i++){ System.out.println(prices[i]); }
默认初始化值:
①整数型数组:0 ②浮点型数组:0.0 ③字符型数组:0或者'\u000'(注意不是'0') ④布尔型数组:false ⑤引用型数组:null
一维数组的内存分析
Java中的内存区域划分为5个部分:程序计数器、虚拟机栈、本地方法栈、堆、方法区
> 与目前数组相关的内存结构: 比如: int [ ] arr = new int [ ]{1,2,3};
>虚拟机栈: 用于存放方法中声明的变量。比如:arr
>堆: 用于存放数组的实体 (即数组中的所有元素) 比如:1,2,3
举例说明:
二维数组
二维数组的理解: 可以看做是一维数组array1又作为另一个一维数组array2的元素而存在。其实,从数组底层的运行机制来看,其实没有多维数组。
- 二维数组的初始化
//二维数组静态初始化: int[][] arr1 = new int[][] {{1,2,3},{4,5},{6,7,8}}; //二维数组动态初始化1: String[][] arr2 = new String[3][4]; //二维数组动态初始化2: double[][] arr3 = new double[2][]; //其他初始化: int arr4[][] = new int[][]{{1,2,3},{4,6},{8,3}}; int [] arr5[] = new int [][]{{2,5},{3,7}}; int arr6[][] = {{2,3},{5,3,7},{3,6}}; //类型推断 String arr7[][] = new String[3][7];
- 二维数组元素的默认初始化值
动态初始化方式1(比如:int[][] arr1 = new int[3][4])
1) 外层元素,默认初始化地址值。
2) 内层元素,默认与一维数组元素的不同类型的默认值规定相同。
> 整型数组元素的默认初始化值:0
> 浮点型数组元素的默认初始化值:0.0
> 字符型数组元素的默认初始化值:0 (或理解为 '\u0000')
> boolean型数组元素的默认初始化值:false
> 引用数据类型数组元素的默认初始化值:null
动态初始化方式2 (比如:int[][] arr = new int[3][])
1) 外层元素,默认存储null
2) 内存元素,不存在。如果调用会报错(NullPointerException)
//二维数组动态初始化1: int[][] arr1 = new int[3][2]; //外层元素默认值: System.out.println(arr1[0]); //[I@776bec8f System.out.println(arr1[1]); //[I@4eec7777 //内层元素默认值: System.out.println(arr1[0][0]); //0 boolean[][] arr2 = new boolean[3][4]; //外层元素默认地址: System.out.println(arr2[0]); //地址值 //内层元素默认值: System.out.println(arr2[0][1]); //false //二维数组动态初始化2: int[][] arr3 = new int[4][]; //外层元素默认值: System.out.println(arr3[0]); //null 因为分配了四个空间,是引用类型,但是没有指向,所以默认null //内层元素默认值: System.out.println(arr3[0][0]); //报错
- 二维数组的调用
//二维数组静态初始化: int[][] arr1 = new int[][] {{1,2,3},{4,5},{6,7,8}}; //调用内层元素 System.out.println(arr1[0][0]); //1 System.out.println(arr1[2][1]); //7 //调用外层 System.out.println(arr1[0]); //[I@776ec8df 这里会输出地址而不是{1,2,3}
- 二维数组的长度和遍历
//二维数组静态初始化: int[][] arr1 = new int[][] {{1,2,3},{4,5},{6,7,8}}; //数组的长度 System.out.println(arr1.length); //3 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(); }
二维数组的内存解析
注意:arr2[2][2] = 1; 因为还没开辟空间所以找arr2[2][2]根本找不到,会报错NullPointerException
二维数组的赋值问题
int[] arr1 = new int[10]; byte[] arr2 = new byte[20]; // arr1 = arr2; 编译不通过! 原因:int[]、byte[]是两种不同类型的引用变量 int[][] arr3 = new int[3][2]; // arr3 = arr1; 编译不同。 维度都不相同 arr3[0] = arr1; //编译正确! System.out.println(arr3[0]); //[I@776ec8df System.out.println(arr1); //[I@776ec8df System.out.println(arr3); //[[I@3b07d329
数组的算法
-
基本算法
//动态初始化创建数组 int[] arr = new int[10]; //通过循环给数组赋值: 赋值为两位数[10-99] for (int i = 0; i < arr.length; i++){ arr[i] = int(Math.random()*90)+10; } //求最大值: int max = arr[0]; for (int i = 1; i < arr.length; i++){ if (arr[i] > max){ max = arr[i]; } } System.out.println("最大值是:" + max); //求最小值: int min = arr[0]; for (int i = 1; i < arr.length; i++){ if (arr[i] < max){ min = arr[i]; } } System.out.println("最大值是:" + min); //求总和: int sum = 0; for (int i = 0; i < arr.length; i++){ sum += arr[i]; } System.out.println("总和是:" + sum); //求平均值: int avg = sum/arr.length; System.out.println("平均值是:" + avg);
问题: 对于一个长度6的int型数组,随机赋值1-30,但是数组中不能有重复的数 [注意!]
//动态初始化创建数组 int[] arr = new int[6]; for (int i = 0; i < arr.length; i++){ arr[i] = (int)(Math.random()*30)+1; boolean flag = true; for (int j = 0; j < i; j ++){ if (arr[i] == arr[j]){ i--; break; } } System.out.println(arr[i]); }
-
回形数
import java.util.Scanner; public class RectangleTest{ public static void main(String[] args){ Scanner scan = new Scanner(System.in); System.out.println("请输入一个数字:"); int num = scan.nextInt(); int arr[][] = new int[num][num]; int turn = num/2; int length = arr[0].length; int sum = 1; for (int i = 0; i < turn; i++){ for (int j = 0; j < 4; j++){ for(int k = 0; k < length-2*i-1; k++){ switch (j){ case 0: arr[i][i+k] = sum; sum += 1; continue; case 1: arr[i+k][length-i-1] = sum; sum += 1; continue; case 2: arr[length-i-1][length-k-1-i] = sum; sum += 1; continue; case 3: arr[length-k-1-i][i] = sum; sum += 1; } } } } if(num%2 == 1){ arr[turn][turn] = sum; } for (int i = 0; i < length; i ++){ for (int j = 0 ; j < length; j ++){ System.out.print(arr[i][j]+"\t"); } System.out.println(); } } }
其他优秀方法:
public class HelloWorld { public static void main(String []args) { //动态初始化创建数组 Scanner scan = new Scanner(System.in); System.out.println("请输入一个数字:"); int n = scan.nextInt(); int[][] arr = new int [n][n]; int count = 0; //要显示的数据 int maxX = n-1; // x轴的最大下标 int maxY = n-1; // y轴的最大下标 int minX = 0; //x轴最小下标 int minY = 0; //y轴最小下标 while(minX <= maxX){ for (int x = minX; x <= maxX; x++){ arr[minY][x] = ++ count; } minY++; for (int y = minY; y <= maxY; y++){ arr[y][maxX] = ++ count; } maxX--; for(int x = maxX; x >= minX; x--){ arr[minY][x] = ++count; } maxY--; for(int y = maxY; y >= minY; y--){ arr[y][minX] = ++count; } minX++; } for (int i = 0; i < arr.length; i ++){ for (int j = 0 ; j < arr.length; j ++){ String space = (arr[i][j] + "").length()==1 ? "0" : ""; System.out.println(space + arr[i][j]); } System.out.println(); } } }
-
数组的值复制
注意:不可以写arr2 = arr1; //这样得到的是arr1的地址,改动arr2,arr1里面的值一起在变化。
int[] arr1,arr2; arr1 = new int[]{1,2,3,4,5,6}; //复制arr1的数组给arr2 arr2 = new int[arr1.length]; for (int i = 0; i < arr2.length; i++){ if (i%2 == 0){ arrw[i]=i; } }
-
数组的反转
int[] arr1 = new int[]{34,65,3,56,58,24,98,78,45}; //反转方式1 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; } //反转方式2 for(int i = 0, j = arr.length-1; i < j; i++, j--){ //交互arr[i] 与 arr[j]位置的元素 int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } //反转方式3 不推荐 int[] newArr = new int[arr.length]; for(int i = arr.length-1; i >= 0; i--){ newArr[arr.length-1-i] = arr[i]; }
-
数组的扩容与缩容
int[] arr = new int[]{1,2,3,4,5}; //扩容1倍的容量 int[] newArr = new int[arr.length * 2]; //将原有数组中的元素复制到新的数组中: for (int i = 0; i <arr.length; i++){ newArr[i] = arr[i]; } //将新数组的地址赋值给原有的数组变量 arr = newArr;
int[] arr = new int[]{1,2,3,4,5,6}; //删除索引第四个的元素 int deleteIndex = 4; //方式1 不新建数组 for (int i = deleteIndex; i < arr.length - 1; i++){ arr[i] = arr[i + 1]; } arr[arr.length-1] = 0; //方式2 新建数组,比原有数组长度少一个 int[] newArr = new int[arr.length - 1]; for(int i = 0; i < deleteIndex; i++){ newArr[i] = arr[i]; } for(int i = deleteIndex; i < arr.length-1; i++){ newArr[i] = arr[i+1]; } arr = newArr;
-
数组的查找
顺序查找
优点: 算法简单 缺点: 效率低,执行的时间复杂度为O(n)
int[] arr = new int[]{34,65,23,6,36,62,74,13}; int target = 6; //线性查找 int i = 0; for(; i < arr.length; i++){ if(target == arr[i]){ system.out.println("找到了!"+target+",对应的位置是:"+i); break; } } if (i == arr.length){ system.out.println("不好意思! 没有找到此元素!"); }
二分查找
优点: 执行效率高,执行的时间复杂度O(log2N) 缺点:相对顺序查找难一点
int[] arr = new int[]{2,4,6,9,11,18,32,57,74,82}; int target = 11; //二分查找 必须有序数组 int head = 0;// 默认的首索引 int end = arr.length - 1;// 默认的尾索引 boolean flag = false; while(head <= end){ int middle = (head + end) / 2; if(target == arr[middle]){ System.out.println("找到了!" + target + ",对应的位置为:" + middle); flag = true; break; }else if (target > arr[middle]){ head = middle + 1; }else{ end = middle - 1; } } if (!flag){ System.out.println("不好意思!没有找到!"); }
-
数组的排序
时间复杂度:分析关键字的比较次数和记录的移动次数
(log 是以2为底)
空间复杂度: 分析算法中需要多少辅助内存。 一个算法的空间复杂度S(n) 定义为该算法所耗费的存储空间, 它也是问题规模n的函数。
稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。
排序的分类:内部排序(内存中排序) 外部排序(外部存储设备+内存)
冒泡排序
时间复杂度O(n^2)
基本思想:对待排序的元素从前向后依次比较相邻的两个元素,如果顺序不对则交换它们的位置,一轮比较下来,最大的元素就会“冒泡”到数组的末尾。
int[] arr = new int[]{34,2,76,67,15,95,57}; //冒泡排序 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] + " "); }
快速排序
快速排序的时间复杂度O(nlog2n)!!
基本步骤:
-
首先选取待排序数组中一个元素作为基准元素,通常选择第一个元素或最后一个元素作为基准元素。
-
遍历数组,将小于基准元素的元素放到左边,大于等于基准元素的元素放到右边,此时数组被划分成了两个部分。
-
对左半部分和右半部分分别递归执行上述操作,直到排序完成。
public static void quickSort(int[] arr,int low,int high){ int i,j,temp,t; if(low>high){ return; } i=low; j=high; //temp就是基准位 temp = arr[low]; 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[low] = arr[i]; arr[i] = temp; //递归调用左半数组 quickSort(arr, low, j-1); //递归调用右半数组 quickSort(arr, j+1, high); } public static void main(String[] args){ int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19}; quickSort(arr, 0, arr.length-1); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } }
Arrays数组工具类
Arrays类所在位置:处在java.util包下
-
boolean equals(int[] a, int[] b); 比较两个数组的元素是否依次相等
// boolean equals(int[] a, int[] b); 比较两个数组的元素是否依次相等 int[] arr1 = new int[]{1,2,3,4,5}; int[] arr2 = new int[]{1,2,3,4,5}; System.out.println(arr1 == arr2); //这一句比的是这两个数组的地址!!! //正确做法: boolean isEquals = Arrays.equals(arr1,arr2); System.out.println(isEquals);
-
String toString (int[] a); 输出数组元素信息
// String toString(int[] a); 输出数组元素信息 int[] arr1 = new int[]{1,2,3,4,5}; System.out.println(arr1); //这里输出的实arr1的地址 //正确做法: System.out.println(Arrays.toString(arr1));
-
void fill(int[] a, int val); 将指定值填充到数组之中
// void fill(int[] a, int val); 将指定值填充到数组之中 int[] arr1 = new int[]{1,2,3,4,5}; Arrays.fill(arr1,10); System.out.println(Arrays.toString(arr1));
-
void sort (int[] a);使用快速排序算法对数组排序
// void sort (int[] a);使用快速排序算法对数组排序 int[] arr1 = new int[]{1,2,3,4,5}; Arrays.sort(arr1); System.out.println(Arrays.toString(arr1));
-
int binarySearch(int[] a, int key); 二分查找
适用前提: 数组必须是有序的! 如果返回的索引是负数就是没找到!
// int binarySearch(int[] a, int key); 二分查找 int[] arr1 = new int[]{1,2,3,4,5}; int index = Arrays.binarySearch(arr1,4); if (index >= 0 ){ System.out.println("找到了!索引位置是:" + index); }else { System.out.println("未找到!"); }
数组中的异常
> 数组角标越界的异常: ArrayIndexOutOfBoundsException
> 空指针的异常: NullPointerException
// 数组角标越界的异常 int[] arr = new int[10]; //角标的有效范围:0-9 System.out.println(arr[10]); //越界 System.out.println(arr[-1]); //越界
// 数组空指针异常 //情况1 int[] arr1 = new int[10]; arr1 = null; System.out.println(arr1[0]); //空指针 //情况2 int[][] arr2 = new int[3][]; System.out.println(arr2[0][1]); //空指针 //情况3 String[] arr3 = new String[4]; System.out.println(arr3[0].toString()); //arr3[0]是null 所以不可以调用里面的方法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!