数组的定义
数组就是用来存储一批同种类型数据的内存区域(可以理解成容器)。
我们简单的认识一下数组:
// 20, 10, 80, 60, 90
int[] arr = {20, 10, 80, 60, 90};
// 牛二, 西门, 全蛋
String[] names = {"牛二", "西门", "全蛋"};
那关于数组我们需要学会什么
- 在程序中如何去定义数组存储数据,具体格式是什么样的
- 怎么去获取,修改数组中的数据
- 怎么结合数组解决一些实际业务
- 数组在内存中具体是怎么去工作的
- 避免入坑,同时可以在出现问题后立即排查问题所在
静态初始化数组
静态初始化数组: 定义数组的时候直接给数组赋值。
静态初始化数组的格式如下:
- 完整格式:
数据类型[] 数组名 = new 数据类型[] {元素1, 元素2, 元素3, ...};
double[] scores = new double[]{89.9, 99.5, 59.5, 88.0};
int[] ages = new int[]{12, 24, 36}
- 简化格式:
数据类型[] 数组名 = { 元素1,元素2 ,元素3,… };
int[] ages = {12, 24, 36};
- 在内存中的表现如下
注意:数组变量名中存储的是数组在内存中的地址,数组是引用类型。
数组的访问
数组元素的访问格式: 数组名称[索引]
- 索引从0开始, 索引0代表第一个数组元素, 以此类推
public static void main(String[] args) {
int[] ages = {12, 24, 36};
// 获取数组元素
System.out.println(ages[0]); // 12
System.out.println(ages[1]); // 24
System.out.println(ages[2]); // 36
// 获取数组元素并且重新赋值
ages[2] = 40;
System.out.println(ages[2]); // 40
}
数组的长度属性: 数组名称.length
// 获取数组的长度(也就是数组元素的个数)
System.out.println(ages.length); // 3
- 那么数组的最大索引值该怎么表示? 其实长度减一就是最大索引值
System.out.println(ages.length - 1); // 2
数组的注意事项
数据类型[] 数组名
也可以写成 数据类型 数组名[]
。
int[] ages =...;
int ages[] =...;
double[] scores = ...;
double scores[] = ...;
什么类型的数组只能存放什么类型的数据,否则报错。
// 错误写法, 不能放其他数据类
int[] arr = new int[]{10, 20, "kaisa"}
数组一旦定义出来,程序执行的过程中,长度、类型就固定了。
动态初始化数组
动态初始化数组: 定义数组的时候只确定元素的类型和数组的长度,之后再存入具体数据。
数组的动态初始化格式: 数组类型[] 数组名称 = new 数据类型[数组长度]
// 先确定元素的类型和数组的长度
int[] arr = new int[3];
// 再赋值
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
System.out.println(arr[0]); // 10
System.out.println(arr[1]); // 20
System.out.println(arr[2]); // 30
元素默认值规则: 当我们有定义数组的长度和类型, 没有赋值时, 数组元素的默认值遵守以下规则
数据类型 | 明细 | 默认值 |
---|---|---|
基本类型 | byte、short、char、int、long | 0 |
基本类型 | float、double | 0.0 |
基本类型 | boolean | false |
引用类型 | 类、接口、数组、String | null |
两种初始化的的使用场景总结、注意事项说明:
- 动态初始化:只指定数组长度,后期赋值,适合开始知道数据的数量,但是不确定具体元素值的业务场景。
- 静态初始化:开始就存入元素值,适合一开始就能确定元素值的业务场景。
- 两种格式的写法是独立的,不可以混用。
数组的遍历
数组的遍历介绍:
- 数组遍历: 就是一个一个的访问数组的所有元素
- 为什么要遍历? 搜索, 数据统计等等都需要用到遍历
我们可以使用for循环遍历数组, 如下:
public static void main(String[] args) {
int[] nums = {10, 20, 30, 40};
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}
案例: 遍历数组求和
需求: 某部门5名员工的销售额分别是:16、26、36、6、100,请计算出他们部门的总销售额。
分析:
- 把这5个数据放到数组中
- 遍历数组中每个元素, 然后在外面定义求和变量把他们累加起来。
代码演示:
public static void main(String[] args) {
int totalMoney = 0;
int[] money = {16, 26, 36, 6, 100};
for (int i = 0; i < money.length; i++) {
totalMoney+=money[i];
}
System.out.println(totalMoney); // 184
}
案例: 数组元素求最大值
例如我们有一个int类型数组, 我们如何求出数组元素中的最大值
分析:
- 把颜值数据拿到程序中去,用数组装起来。
- 定义一个变量用于记录最大值,这个变量建议默认存储第一个元素值作为参照。
- 遍历数组的元素,如果该元素大于变量存储的元素,则替换变量存储的值为该元素。
- 循环结束后输出最大值变量即可。
演示代码:
public static void main(String[] args) {
int[] nums = {168, 120, 68, 68, 931, 654, 264};
// 定义一个变量保存最大值
int max = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
// 输出最大值
System.out.println(max);
}
数组的排序
数组排序: 就是对数组中的元素,进行升序(由小到大)或者降序(由大到小)的操作。
数组排序的技术:
-
冒泡排序
-
选择排序
-
快速排序
-
插入排序
-
…
数组搜索相关的技术:
-
二分搜索
-
分块查找
-
哈希表查找
-
…
目前我们先学习冒泡排序
冒泡排序的思想:
- 每次从数组中找出最大值放在数组的后面去。
实现冒泡排序的关键步骤分析:
- 确定总共需要做几轮:
数组的长度-1
- 每轮比较几次:
数组的长度 - 数组的第几轮
- 当前位置大于后一个位置则交换数据
演示代码:
public static void main(String[] args) {
int[] nums = {168, 120, 68, 68, 931, 654, 264};
// 设置变量用于替换数组元素位置
int transfer = 0;
// 定义一个外部循环控制总共需要冒几轮
for (int i = 1; i < nums.length - 1; i++) {
// 定义一个内部循环,控制每轮依次往后比较几个位置
for (int j = 0; j < nums.length - i); j++) {
// 满足条件则替换数组元素位置
if (nums[j] > nums[j+1]) {
transfer = nums[j];
nums[j] = nums[j+1];
nums[j+1] = transfer;
}
}
}
// 循环遍历数组
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}
数组的内存
Java 中内存分配为如下几种:
- 栈
- 堆
- 方法区
- 本地方法栈
- 寄存器
我们只需要学习前三种
-
方法区: 字节码文件加载时进入的内存
-
栈内存: 方法运行时所进入的内存变量也是在这里
-
堆内存: new 出来的东西会在这块内存中开辟空间并产生地址
例如有如下代码:
public class Test {
public static void main(String[] args) {
int a = 10;
int[] arr = new int[]{11, 22, 33};
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
-
Test类和main方法会放在方法区, 然后运行main方法会在栈内存中运行
-
定义的变量a, 会直接在栈内存中保存变量a的值
-
定义的数组, 会在堆内存中开辟一块空间, 用于保存数组的元素, 而栈内存中保存的是指向堆内存中数组的地址
下面我用一幅图展示在内存中的表现吧
我们再来看一段代码:
public class Test2{
public static void main(String[] args) {
int[] arr1 = {11, 22, 33};
int[] arr2 = arr1;
// 两个数组指向堆内存中同一个地址
System.out.println(arr1); // [I@776ec8df
System.out.println(arr2); // [I@776ec8df
// 改变一个数组元素的值, 另一个也会改变
arr2[1] = 99;
System.out.println(arr1[1]); // 99
System.out.println(arr2[1]); // 99
}
}
- 这是因为数组arr1和arr2在栈内存中保存的地址是一样的, 我们来看看内存图吧
数组使用常见问题
问题1:如果访问的元素位置超过最大索引,执行时会出现ArrayIndexOutOfBoundsException(数组索引越界异常)
int[] arr = new int[]{11, 22, 33};
System.out.println(arr[2]); // 正常访问
// System.out.println(arr[3]) // 出现异常
- 会出现ArrayIndexOutOfBoundsException异常
问题2:如果数组变量中没有存储数组的地址, 而是null, 在访问数组信息时会出现NullPointerException(空指针异常)
arr = null;
System.out.println(arr); // null
// System.out.println(arr.length) // 出现异常
- 会出现NullPointerException异常
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/120110.html