😹 作者: gh-xiaohe
😻 gh-xiaohe的博客
😽 觉得博主文章写的不错的话,希望大家三连(✌关注,✌点赞,✌评论),多多支持一下!!
💒 数组
🚏 数组的定义
数组是什么
- 数组就是用来存储一批同种类型数据的内存区域(可以理解成容器)。
例子
结论:数组适合做一批同种类型数据的存储。
🚀 静态初始化数组
- 定义数组的时候直接给数组赋值。
🚬 静态初始化数组的格式:
public class ArrayDemo1 {
public static void main(String[] args) {
// 目标:学会使用静态初始化的方式定义数组
// 数据类型[] 数组名称 = new 数据类型[]{元素1,元素2,...}
// double[] scores = new double[]{99.5, 88.0, 75.5};
double[] scores = {99.5, 88.0, 75.5}; // 简化写法
System.out.println(scores + " ---> " + "输出的是数组的地址");
// int[] ages = new int[]{12, 24, 36};
int[] ages = {12, 24, 36};
System.out.println(ages + " ---> " + "输出的是数组的地址");
// String[] names = new String[]{"牛二", "全蛋儿", "老王"};
String[] names = {"牛二", "全蛋儿", "老王"};
System.out.println(names + " ---> " + "输出的是数组的地址");
System.out.println("names.length = " + names.length);
System.out.println("names[0] = " + names[0]);
System.out.println("names[1] = " + names[1]);
System.out.println("names[2] = " + names[2]);
}
}
🚬 静态初始化数组的基本原理
注意:数组变量名中存储的是数组在内存中的地址,数组是引用类型。
🚭 总结
-
数组的静态初始化的写法和特点什么样的?
-
数组是属于什么类型,数组变量名中存储的是什么?
- 引用数据类型,存储的数组在内存中的地址信息。
🚬 数组的访问
数组的访问
数组的长度属性:length
问题:数组的最大索引可以怎么表示?
public class ArrayDemo2 {
public static void main(String[] args) {
// 目标:学会访问数组的元素
int[] ages = {12, 24, 36};
// 0 1 2
// 取值: 数组名称[索引]
System.out.println(ages[0]); // 12
System.out.println(ages[1]); // 24
System.out.println(ages[2]); // 36
// 赋值:数组名称[索引] = 数据;
ages[2] = 100;
System.out.println(ages[2]);// 100
// 访问数组的长度
System.out.println(ages.length); // 3
int[] arr = {};
System.out.println(arr.length - 1);// -1
}
}
🚭 总结
-
如何访问数组的元素?
-
如何访问数组的长度?
- 数组名称.length
-
数组的最大索引怎么获取?
🚬 数组的几个注意事项
-
“数据类型[] 数组名”也可以写成。 “数据类型 数组名[] ”
-
什么类型的数组存放什么类型的数据,否则报错。
-
数组一旦定义出来,程序执行的过程中,长度、类型就固定了。
public class ArrayAttentionDemo3 {
public static void main(String[] args) {
// 目标:理解数组的注意事项
// 1、数据类型[] 数组名称 也可以写成 数据类型 数组名称[]
int[] ages = {11, 23, 45};
// int ages1[] = {11, 23, 45};
// 2、什么类型的数组只能存放什么类型的元素
// String[] names = {"西门吹雪", "独孤求败", 23}; // 错误的
// 3、数组一旦定义出来之后,类型和长度就固定了
int[] ages2 = {11, 23, 45};
System.out.println(ages2[3]); // 报错! 长度固定是3了不能访问第4个元素!! 数组越界!!
}
}
🚄 动态初始化数组
-
定义数组的时候只确定元素的类型和数组的长度,之后再存入具体数据。
public class ArrayDemo4 { public static void main(String[] args) { // 目标:学会动态初始化数组的定义和使用。 double[] scores = new double[3]; // [0.0, 0.0, 0.0] // 0 1 2 // 赋值 scores[0] = 99.5; System.out.println(scores[0]); // 99.5 System.out.println(scores[2]); // 未赋值,默认为0.0 String[] names = new String[90]; names[0] = "迪丽热巴"; // 这里不能直接赋值,因为数组的类型是Object,所以需要转换 names[2] = "马尔扎哈"; System.out.println(names[0]); // 迪丽热巴 System.out.println(names[1]); // null System.out.println(names[2]); // 马尔扎哈 } }
🚭 总结
-
动态初始化的写法是什么样的?
-
两种数组定义时的特点和场景有什么区别
- 当前已经知道存入的元素值,用静态初始化。
- 当前还不清楚要存入哪些数据,用动态初始化。
🚬 动态初始化数组的元素默认值
🚭 元素默认值规则
两种初始化的的使用场景总结、注意事项说明:
public class ArrayDemo5 {
public static void main(String[] args) {
// 目标:掌握动态初始化元素默认值的规则。
// 1、整型数组的元素默认值都是0
int[] arr = new int[10];
System.out.println(arr[0]); // 0
System.out.println(arr[9]); // 0
// 2、字符数组的元素默认值是多少呢? 0
char[] chars = new char[100];
System.out.println((int)chars[0]); // 0
System.out.println((int)chars[99]); // 0
// 3、浮点型数组的元素默认值是0.0
double[] scores = new double[90];
System.out.println(scores[0]); // 0.0
System.out.println(scores[89]); // 0.0
// 4、布尔类型的数组
boolean[] booleans = new boolean[100];
System.out.println(booleans[0]); // false
System.out.println(booleans[99]); // false
// 5、引用类型的数组
String[] names = new String[90];
System.out.println(names[0]); // null
System.out.println(names[89]); // null
// int[] arrs = new int[3]{30, 40, 50}; // 两种格式的写法是独立的,不可以混用。
int[] a = {1,2,3};
int[] b = {1,2,3};
System.out.println(a); // [I@1c9d8f0
System.out.println(b); // [I@1c9d8f0
}
}
🚭 总结
- 动态初始化数组后元素的默认值是什么样的?
- byte、short、int 、char、long类型数组元素的默认值都是0
- float、double类型数组元素的默认值都是0.0
- boolean类型数组元素的默认值是false、String类型数组元素的默认值是null
🚏 数组的遍历
🚀 数组遍历介绍
- 遍历:就是一个一个数据的访问。
- 为什么要遍历? 搜索、数据统计等等都需要用到遍历。
public class ArrayDemo {
public static void main(String[] args) {
// 目标:学会进行数组元素的遍历
int[] arr = {12, 24, 12, 48, 98};
// 0 1 2 3 4
// 原始遍历方式
// System.out.println(arr[0]);
// System.out.println(arr[1]);
// System.out.println(arr[2]);
// System.out.println(arr[3]);
// System.out.println(arr[4]);
// for (int i = 0; i < 5; i++) {
// System.out.println(arr[i]);
// }
// 终极数组遍历形式
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
🚄 案例
🚬 数组元素求和
需求:某部门5名员工的销售额分别是:16、26、36、6、100,请计算出他们部门的总销售额。
public class Test1 {
public static void main(String[] args) {
// 需求:数组元素求和
// 1、把这些数据拿到程序中使用数组记住
int[] money = {16, 32, 8, 100, 78};
// 3、定义一个求和变量累加数组的元素值
int sum = 0;
// 2、遍历数组中的每个元素
for (int i = 0; i < money.length; i++) {
// 拿到每个元素值累加
sum += money[i];
}
// 4、输出求和变量即可
System.out.println("数组的元素和是:" + sum);
}
}
🚬 数组求最值
public class Test2 {
public static void main(String[] args) {
// 需求:数组元素求最值。
// 1、定义一个静态初始化的数组,存储一批颜值。
int[] faceScore = {15, 9000, 10000, 20000, 9500, -5};
// 0 1 2 3 4 5
// 2、定义一个变量用于存储最大值元素,建议使用第一个元素作为参照。
int max = faceScore[0];
// 3、遍历数组的每个元素,依次与最大值变量的数据比较,若较大,则替换。
for (int i = 1; i < faceScore.length; i++) {
if(faceScore[i] > max){
max = faceScore[i];
}
}
// 4、输出最大值变量存储的数据即可。
System.out.println("数组的最大值是:" + max);
}
}
🚬 猜数字游戏
游戏规则如下:
游戏后台随机生成1-20之间的5个数(无所谓是否重复),然后让大家来猜数字:
未猜中提示:“未命中”,并继续猜测
猜中提示:“运气不错,猜中了”,并输出该数据第一次出现的索引位置,最后把数组中的5个数据都输出看以下, 然后结束本游戏。
public class test10086 {
public static void main(String[] args) {
// 定义一个数组,用来存放随机数
int[] arr = new int[5];
// 生成 1-20 区间的五个随机数
for (int i = 0; i < 5; i++) {
Random random = new Random();
int j = random.nextInt(1, 21);
arr[i] = j;
}
// 控制台输出
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入一个 1-20 数字:");
int i = scanner.nextInt();
if (i == arr[0] || i == arr[1] || i == arr[2] || i == arr[3] || i == arr[4]) {
System.out.println("您已经猜中了该数据,运气不错了!您猜中的数据索引是:" + i);
// 遍历数组
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[j] + " ");
}
break;
} else if (i > 20 || i < 0) { //
System.out.println("输入有误,请重新输入!");
} else {
System.out.println("很遗憾,猜错了!");
}
}
}
}
🚬 随机排名经典
某公司开发部5名开发人员,要进行项目进展汇报演讲,现在采取随机排名后进行汇报。
请先依次录入5名员工的工号,然后展示出一组随机的排名顺序。
/*
① 先输入员工的员工号
② 把输入的员工号 存储到数组中
③ 生成一个 指定范围的 随机索引 与数组 索引进行交换
④ 重新打印
**/
public class Test4 {
public static void main(String[] args) {
// 目标:键盘录入一组工号,最终要随机输出一组出来作为排名
Scanner sc = new Scanner(System.in);
System.out.println("输入需要多少个员工进行排序:" );
int a1 = sc.nextInt();
// 1、动态初始化一个数组
int[] codes = new int[a1]; // 动态生成
// 2、定义一个循环,循环5次,依次录入一个工号存入对应的位置
// Scanner sc = new Scanner(System.in);
for (int i = 0; i < codes.length; i++) {
// 正式录入工号
System.out.println("请您输入第" + (i + 1) + "个员工的工号:");
int code = sc.nextInt();
// 存入到数组中去
codes[i] = code;
}
// 3、遍历数组中的每个元素,然后随机一个索引出来,让该元素与随机索引位置处的元素值进行交换(本节的重点)
// codes = [12, 36, 28, 45, 99]
Random r = new Random();
for (int i = 0; i < codes.length; i++) {
// 当前遍历的元素值:codes[i]
// 随机一个索引位置出来:codes[index]
int index = r.nextInt(codes.length);
// 定义一个临时变量存储index位置处的值
int temp = codes[index];
codes[index] = codes[i];
codes[i] = temp;
}
// 4、遍历数组元素输出就是随机排名的结果
for (int i = 0; i < codes.length; i++) {
System.out.print(codes[i] + "\t");
}
}
}
🚏 数组的内存图
🚀Java内存分配
java内存分配
🚄 数组内存图
数组内存分配
🚒 两个变量指向同一个数组
- 赋的不是数组对象,而是地址
两个变量指向同一个数组对象
🚏 数组使用的常见问题
🚬 问题1:
- 如果访问的元素位置超过最大索引,执行时会出现ArrayIndexOutOfBoundsException(数组索引越界异常)
🚬 问题2:
- 如果数组变量中没有存储数组的地址,而是null, 在访问数组信息时会出现NullPointerException(空指针异常)
public class test10086 {
public static void main(String[] args) {
//数组角标越界的异常:ArrayIndexOutOfBoundsException
int[] arr = new int[]{1, 2, 3, 4, 5};
for (int i = 0; i <= arr.length; i++) { //多写个等号
System.out.println(arr[i]);
}
System.out.println(arr[-2]);
//空指针异常:NullPointerException
//情况一:
/**
int[] arr1=new int[] {1,2,3};
arr1=null;
System.out.println(arr1[0]);
一维数组的出现空指针的情况:
看数组是不是没有造 或者 后面赋值赋一个null
*/
//情况二:
int[][] arr2 = new int[4][];
System.out.println(arr2[0][0]);
//情况三:
String[] arr3 = new String[]{null, "bb", "cc"};
System.out.println(arr3[0].toString());
//与上面表示的一样
String[] arr4 = new String[]{"aa", "bb", "cc"};
arr3[0] = null;
System.out.println(arr3[0].toString());
}
}
🚏 二维数组
/*
二维数组的使用:
1.理解:我们可以看成一堆数组arrary1又作为另一个一维数组array2的元素而存在。
其实,从数组底层的运行机制开看,其实没有多维数组。
2.二维的使用:
(1)二维数组的声明和初始化
(2)如何调用数组的指定位置的元素: 通过角标的方式调用
(3)如何获取数组的长度: 属性:length:
(4)如何遍历二维数组 遍历:表示从同才尾经历一遍
(5)数组元素默认的初始化值
1)基本数据类型:
①数组元素是整型:0
②数组元素是浮点型:0.0
③数组元素是char型:0 或'\u0000',而非'0' 大致为空格的意思
④数组元素是boolean型: false
2)引用数据类型: null 空值
(6)数组的内存解析
*/
public class test1 {
public static void main(String[] args) {
//1.二维数组的声明和初始化
int[] arr =new int[] {1,2,3};//一维数组
//静态初始化
int[][] arr1=new int[][]{{1,2,3},{4,5},{6,7}};//二维数组
//动态初始化1
String[][] arr2=new String[5][7];
//动态初始化2
String[][] arr3=new String[3][];
//同是正确写法:
int[] arr4[]=new int[][]{{1,2,3},{4,5},{6,7}};
int[] arr5[]={{1,2,3},{4,5},{6,7}};
//2.如何调用数组的指定位置的元素:
//静态
System.out.println(arr1[2][1]);//第一个[]表示第几个{}第二个[]表示括号里第几个数
System.out.println(arr1[0][2]);//特别注意:数组是从0开始的
arr3[1] =new String[4];
System.out.println(arr3[1][0]);//null
//3.如何获取数组的长度:
System.out.println(arr4.length);//表示数组里面有几个{}为数组的长度
System.out.println(arr4[0].length);//表示数组第一个{}里面数组的长度{1,2,3} 3
System.out.println(arr4[1].length);//表示数组第一个{}里面数组的长度{4,5} 2
//4.如何遍历二维数组
for(int i=0;i<arr4.length;i++) {
for(int j=0;j<arr4[i].length;j++) {
System.out.print(arr4[i][j]+" ");
}
System.out.println();
}
//5.数组元素默认的初始化值
}
}
/*
二维数组的使用:
规定:二维数组分为外层数组元素,内层数组元素
int[][] arr = new int [4][3];
外层:arr[0],arr[1]等
内层:arr[0][0],arr[1][2]等
(5)数组元素默认的初始化值
针对于初始化方式一:int[][] arr = new int [4][3];
外层元素的初始化值:地址值
内层元素的初始化值:于一维数组初始化相同
针对于初始化方式二:int[][] arr = new int [4][];
外层元素的初始化值:null
内层元素的初始化值:不能调用,否则报错
(6)数组的内存解析
*/
public class test2 {
public static void main(String[] args) {
int[][] arr = new int [4][3];
System.out.println(arr[0]); //地址值[I@10dea4e [表示一维的 I表示int型
System.out.println(arr[0][0]);//0
System.out.println(arr);//[[I@647e05 二维的
System.out.println("*****************************");
float[][] arr1 = new float [4][3];
System.out.println(arr1[0]); //地址值
System.out.println(arr1[0][0]);//0.0
System.out.println("*******************");
String[][] arr2 = new String [4][2];
System.out.println(arr2[1]); //地址值
System.out.println(arr2[1][1]);//null
//特殊
System.out.println("*******************");
double[][] arr3 = new double [4][];
System.out.println(arr3[1]);//null
//System.out.println(arr3[1][0]);//报错:空指针异常
char [][] arr7 = new char [4][];
System.out.println(arr7[1]);
System.out.println("*");
}
}
🚏 数组的(面试题)
🚬 数组复制
// 数组的复制
public class Copy {
public static void main(String[] args) {
// 数组的复制
/**
使用简单数组
(1)创建一个名为a的类,在main()方法中声明a1和a2两个变量,他们是int[]类型的数组
(2)使用大括号{},把a初始化为8个素数:2,3,5,7,11,13,17,19
(3)显示a的内容
(4)赋值a1变量等于a2,修改a2中的偶索引元素,使其等于索引(如a[0]=0,a[2]=2)打印出a1
思考:
a1和a2是什么关系 a1和a2地址值相同,都指向了堆空间中的唯一的一个数组实体。
拓展:修改题目,实现a1和a2数组的复制
*/
int[] a1, a2;
a1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
for (int i = 0; i < a1.length; i++) {
System.out.print(a1[i] + " ");
}
//赋值a2变量等于a1
//数组的复制:
a2 = new int[a1.length];
for (int i = 0; i < a2.length; i++) {
a2[i] = a1[i];
}
/**
不能称做数组的复制
a2=a1; //修改的是 地址值
*/
//修改a2中的偶索引元素,使其等于索引(如a[0]=0,a[2]=2)打印出a1
for (int i = 0; i < a1.length; i++) {
if (i % 2 == 0) {
a2[i] = i;
}
}
System.out.println();
//打印a1
for (int i = 0; i < a1.length; i++) {
System.out.print(a2[i] + " ");
}
System.out.println();
System.out.println(a1);// [I@58372a00
System.out.println(a2);// [I@4dd8dc3
System.out.println("***************");
String[] arr = new String[]{"AA", "BB", "CC", "DD", "EE"};
//数组的复制(区别于数组变量的赋值:arr1=arr)
String[] arr1 = new String[arr.length];
for (int i = 0; i < arr1.length; i++) {
arr1[i] = arr[i];
System.out.print(arr1[i] + " ");
}
System.out.println();
System.out.println("***************");
System.out.println(arr1); // [Ljava.lang.String;@568db2f2
System.out.println(arr); //[Ljava.lang.String;@378bf509
}
}
🚬 数组反转
// 数组的反转
public class Reversal {
public static void main(String[] args) {
String[] arr = new String[]{"AA", "BB", "CC", "DD", "EE"};
// 数组的反转
// 方式一:
System.out.println("方式一: ");
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; i < arr.length; i++) { // EE DD CC BB AA
System.out.print(arr[i] + " ");
}
//方法一的优化
System.out.println();
System.out.println("方法一的优化 ");
for (int j = 0; j < arr.length; j++) {//遍历 AA DD CC BB EE
for (int i = 0; i <= 0; i++) {
String temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = temp;
}
System.out.print(arr[j] + " ");
}
// 方式二
System.out.println();
System.out.println("方式二 : ");
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++) { //EE BB CC DD AA
System.out.print(arr[i] + " ");
}
}
}
🚬 数组排序(面试题冒泡排序)
- 就是对数组中的元素,进行升序(由小到大)或者降序(由大到小)的操作。
/**
* 数组的排序:
* (一).衡量排序算法的优劣
* 1.时间复杂度:分析关键字的比较次数和记录的移动次数
* 2.空间复杂度:分析排序算法中需要多少辅助内存
* 3.稳定性:若两个记录a和b关键字值相等,但排序后a,b的先后顺序保持不变,则称这种排序是稳定的
* <p>
* (二).内部排序和外部排序
* 1.内部排序:整个排序过程不需要借助外部存储器(如磁盘等),所有的排序操作都在内存中完成。
* 2.外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助
* 于外部存储器(如磁盘等)。
* 外部排序最常见的时多路归并排序。可以认为外部排序是有多次内部排序组成。
* <p>
* <p>
* 十大内部排序算法:
* 1.选择排序:直接选择排序,堆排序(小重点)
* 2.交换排序:冒泡排序(重点),快速排序(重点)
* 3.插入排序:直接插入排序,折半插入排序,Shell排序
* 4.归并排序:(小重点)
* //以上教为常用
* 5.捅式排序:
* 6.基数排序:
* <p>
* <p>
* 冒泡排序:
* 思想:通过对待排列从前到后,依次比较相邻元素的排序码。若发现逆序则交换,使排序码较大的元素逐渐从从前面向后移。
* 复杂度:n方
* 快速排序:通过一趟排序将排序记录分割成独立两部分,其中一部分记录的关键字比另一部分关键字小,则分别对这两部分继续进行排序,
* 直到整个序列有序
* 复杂度:n成log2n 劳哥n
*
*/
public class Sort {
public static void main(String[] args) {
int[] arr = new int[]{43, 25, 74, 78, -59, -89, -56, 78, 15};
//冒泡排序
for (int i = 0; i < arr.length - 1; i++) { //第几大轮 特别注意:这里的i是控制循环次数的,不是数组下标 从 0 开始 次数是 arr.length - 1
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;
//理解
//i=0 1轮 j<arr.length-1 最大值为arr.length-2
//arr.length-2=arr[j] arr[j+1]=arr.length-1 索引为大值
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
{
}
}
}
}
🚭 总结
-
冒泡排序的思想
- 从头开始两两比较,把较大的元素与较小的元素进行交换
- 每轮把当前最大的一个元素存入到数组当前的末尾。
-
冒泡排序的实现步骤。
- 定义一个外部循环控制总共需要冒几轮(数组的长度-1)
- 定义一个内部循环,控制每轮依次往后比较几个位置(数组长度-i-1)。
🚬 数组查找(面试题二分查找)
🚭 线性查找
// 数组的查找 : 二分查找
public class LoopUp {
public static void main(String[] args) {
String[] arr = new String[]{"AA", "BB", "CC", "DD", "EE"};
//数组的查找:线性查找,二分法查找
//线性查找:从头往后查找
String dest = "BB";
boolean isFlag = true;
for (int i = 0; i < arr.length; i++) {
if (dest.equals(arr[i])) { //字符串是equals int型用==就可以
System.out.println("找到了指定的元素,位置为" + i);
isFlag = false;
break;
}
}
if (isFlag) {
System.out.println("很遗憾没有找到来");
}
}
}
🚭 二分查找
// 数组的查找 : 二分查找
public class LoopUp {
public static void main(String[] args) {
//二分法查找:折半查找 比较快
//前提:所要查找的数组要有序
int[] arr2 = new int[]{-98, -65, -34, -32, 0, 15, 48, 79, 89, 333, 456, 789};
int dest1 = -34;
int head = 0;//初始的首索引
int end = arr2.length - 1;//初始的末索引
boolean isFlag1 = true;
while (head <= end) {
int middle = (head + end) / 2;//middle中间值
if (dest1 == arr2[middle]) {
System.out.println("找到指定得到元素,位置为" + middle);
isFlag = false;
break;
} else if (arr2[middle] > dest1) { //中间值比所要找的值大 ——————|————————在左面区域
end = middle - 1;
} else {//arr2[middle]>dest1) //中间值比所要找的值小 ——————|————————在右面区域
head = middle + 1;
}
}
if (isFlag) {
System.out.println("没有找到该元素哦!");
}
}
}
🚏 数组的常见方法
- push 最后的位置新增一个元素
- pop 删除最后一个元素
- shift 删除第一个元素
- unshift 最前面添加一个元素
- splice 指定位置插入一个元素,或者删除一个元素,或者替换调一个元素
- sort 数组排序
- reverse 翻转数组
- 共同点:可以修改数组,引起数组的改变
- filter 过滤数组元素,不影响原数组