这里写目录标题
第五章 方法与数组–数组
5.2 数组
5.2.1 数组基本概念
1. 基本概念
1.数组:集中存储一定量相同类型数据的容器,是一个连续的内存空间
2. 数组有一维数组,二维数组,n维数组
二维数组:一维数组里面继续保存数组,每一个值都是一个一维数组
3.特点:
(1)存储数据长度固定的容器, 数组是一个定长容器, 当定义一个数组时, 必须指定数组的长度(可为0) ,长度指定之后不可以更改
(2)要存储的数据的数据类型要一致
4. 数组解决了相关多个变量多次声明的繁琐
2. 数组动态初始化
1.数组动态初始化: 只指定数组长度不指定数组元素值. (只声明,不初始化)
2.数组的声明:
(1)数据类型[] 数组名称 = new 数据类型[数组长度];
int [ ] arr = new int[10]; //长度为10的int类型数组 ,数组中可以存储10个int类型数据
(2)数据类型 数组名称[] = new 数据类型[数组长度];
int arr[ ] = new int[10]; //长度为10的int类型数组 ,数组中可以存储10个int类型数据
3.详细解释:
(1)数据类型: 指定创建的数组容器可以存储的数据类型
(2)[]: 表示数组
(3)数组名称: 数组本身也是一个变量,用于存储数组的地址的变量名,就是引用,需要遵循标识符的命名规则
(4) = : 赋值符号,将数组的内存空间地址,赋值给数组名称(就是引用)
(5) new: 关键字,创建数组使用的关键字,用于在堆内存中给数组(数组也是变量)开辟存储数据的连续空间
(6) 数据类型: 和前面的数据类型保持一致
(7)数组长度: 表示数组容器中可以存储多少个元素
4.当定义出一个数组时, JVM虚拟机会默认为数组中的每一个元素进行默认的赋初值动作, 根据不同数据类型赋初值。
(1)整型 : 0
(2)浮点类型 : 0.0
(2)char类型 : ‘ ’
(4)boolean类型 : false
(5)引用数据类型 : null, null表示引用数据类型为空
public class Demo01 {
public static void main(String[] args) {
//动态数组的两种初始化方式
int[] arr0 = new int[10]; //最常见的定义方式
int arr1[] = new int[10]; //new 在堆内存中开辟空间
System.out.println(arr0);
}
}
3. 数组静态初始化
1.数组静态初始化: 在创建数组时,直接定义数组同时初始化数组存储的值(声明+初始化)
2.格式:
(1)数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, ..., 元素n};
(2)简化格式: 数据类型[] 数组名 = {元素1, 元素2, ..., 元素n};
(3)数据类型[][] 数组名称 = {{数组1},{数组2}......}
int [][] arr ={{1,2,3},{3,4,5}}; //长度为2的int类型二维数组 ,数组中存储2个int类型一维数组
3.注意事项:
1)在第二个方括号中,不能写数组元素个数
2)元素值的罗列:元素和元素之间,使用逗号分隔
3)罗列出来的元素值,数据类型必须和声明数组时数据类型一致
4)不能分成两行写,测试:先定义后赋值!不符合Java中定义数组的语法结构
class Demo01 {
public static void main(String[] args) {
int[] arr1 = new int[]{11,67,11,0,3};
int[] arr0 = {334,11,67,11,0,3};
}
}
4. 二维数组的声明
二维数组声明方式灵活,常见声明方式有4种
(1)方式1
数据类型 数组名[][] = {{初始值1},{初始值2},{初始值3}}
public class Arr {
public static void main(String[] args) {
//定义一个二维数组
int arr[][] = {{1,2,3},{4,5,6},{7,8,9}};
//遍历二维数组
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
System.out.print(arr[i][j]+" ");
}
}
}
}
(2)方式2
(3)方式3
(4)方式4
5. 数组的访问格式
1.索引: 每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引,注: 索引也称为下标,角标,脚标。
2. 可以通过数组的索引访问到数组中的元素.索引的范围: 0---数组长度-1.
3.索引访问数组中的元素:
数组名[索引] = 数值; //为数组中的元素赋值
变量 = 数组名[索引]; //获取出数组中的元素
class Demo01 {
public static void main(String[] args) {
int[] arr = {334,11,67,11,0,3};
arr[2] = 3;
System.out.println(arr[2]);
}
}
5.2.2 数组的异常
2.2.1 索引越界
1..索引越界异常(数组下标越界):
ArrayyIndexOutOfBoundsException
数组 索引 超出 边界 异常
2.发生索引越界异常原因:
使用的索引在数组中不存在
3.代码分析:
观察一下代码,运行后会出现什么结果
class Demo01 {
public static void main(String[] args) {
int[] arr = {334,11,67,11,0,3};
System.out.println(arr[7]);
}
}
创建数组,赋值3个元素,数组的索引就是0,1,2,没有3索引,因此我们不能访问数组中不存在 将会抛出 ArrayIndexOutOfBoundsException 数组越界异常.在开发中,数组的越界异常
4.越界异常避免方案
不要访问不存在的索引,使用索引时,验证索引在"0--数组长度-1"范围
5.数组的长度属性: 每个数组都具有长度,Java中赋予了数组一个属性,用于获取数组的长度arr.length
2.2.2 空指针异常
1.空指针异常
NullPointerException
空 指针 异常
2.发生空指针异常原因:
当引用数据类型值设置为null(前提),证明这个变量引用没有指向任何堆内存地址,但仍然想取出变量值
注: null,即为空,用于表示在栈内存的引用中,不记录任何堆内存的地址
3.代码分析:
观察一下代码,运行后会出现什么结果
class Demo01 {
public static void main(String[] args) {
int[] arr = {334,11,67,11,0,3};
arr = null;
System.out.println(arr[7]);
}
}
17 arr = null这行代码,意味着变量arr将不会再保存数组的内存地址,也就不允许再操作数组了,
18 因此运行的时候会抛出NullPointerException 空指针异常.在开发中,数组的越界异常是不能出
19
20 4.空指针异常避免方案
21 在使用引用之前,先判断该引用的值是否为null,不为null再去访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5AWWmmua-1638090027190)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211123215034061.png)]
5.2.3 数组的内存分析
1. Java的内存划分和作用
1.内存概念: 内存是计算机中的重要原件,临时存储区域,作用是运行程序.我们编写的程序是存放 必须放进内存中才能运行.
2.Java虚拟机的内存划分Java虚拟机要运行程序,对空间进行了不同区域的划分,因此每一片区域都有特定的处理数据。
2. 数组在内存中的存储
1. 声明的变量在栈中
2. 数组值存储在堆中,拥有连续的内存空间,有自己的内存地址
3. 索引(数组名指向内存地址)
01 1个一维数组的内存图
1.数组引用的是数组值的内存存储地址,是数组在内存中的地址.new出来的内容,都是在堆内存中存储的。
02 2个一维数组的内存图
两个数组:引用数据类型每次new,都在内存中开辟新的空间地址.地址之间没有联系。
03 两个引用指向同一个一维数组空间
多个引用指向同一个数组空间:任意一个引用修改了数组中的内容,其他引用访问到的就是修改后的数据
class Demo01 {
public static void main(String[] args) {
int[] arr = new int[3];
arr[0] = 18;
arr[1] = 9;
int[] arr1 = arr; //把arr的引用赋值给arr1
System.out.println(arr1[0]);
System.out.println(arr1[1]);
System.out.println(arr1[2]);
}
}
04 二位数组的内存空间
1. 二维数组里的每一个一维数组都有自己的内存空间地址,并且多个一维数组的内存地址是连续的,所以使用一个总地址代表所有一维数组内存地址集合
2. 访问某个数据,先指定它所在一维数组在二维数组中的索引,再指定数据在一维数组中的索引,和矩阵差不多的理解
5.2.4 数组常见操作
1.遍历数组元组
1.数组遍历: 就是将数组中的每个元素分别获取出来,这就是遍历
2.分析思路:
通过循环,访问到数组中所有元素的索引,0到lenght-1
通过索引,访问到对应元素,数组名[索引]
public class Demo01 {
//遍历数组
public static void main(String[] args) {
int[] arr = {2,7,88,4,45,0}; //索引有0,1,2,3,4,5
for(int i = 0;i < arr.length - 1;i ++) {
System.out.println(arr[i]);
}
}
}
2.4.2 数组获取最值
1.给定一个数组,求出数组的最大值
2.分析思路(擂台思想):
1)定义变量,保存数组0索引上的元素
2)遍历数组,获取出数组中的每个元素
3)将遍历到的元素和保存数组0索引上值的变量进行比较
4)如果数组元素的值大于了变量的值,变量记录住新的值
5)数组循环遍历结束,变量保存的就是数组中的最大值
😁擂台思想:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-shYepipi-1638090027197)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211123225032339.png)]
import java.util.Arrays;
public class Demo01 {
//数组元素取最值
public static void main(String[] args) {
int[] a = {2,7,88,4,45,0}; //索引有0,1,2,3,4,5
int sum = a[0];
for(int i = 1;i < a.length - 1;i ++) {
if(a[i] > sum) {
sum =a[i];
}
}
System.out.println(sum);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-imyabJoG-1638090027199)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211123172328399.png)]
2.4.3 数组元素交换
1.将数组中指定索引的两个元素交换位置.
2.分析思路:
1)将两个索引位置对应的元素值获取到
2)设计一个临时变量,先存储小索引元素的原值
3)大索引元素值赋值给小索引元素位置
4)临时变量值赋值给大索引元素位置
int [] arr = {2,7,88,4,45,0}; 9
要求:把88和 0 交换一下位置 打印数组
import java.util.Arrays;
public class Demo01 {
//数组元素交换88 与0
public static void main(String[] args) {
int[] a = {2,7,88,4,45,0}; //索引有0,1,2,3,4,5
int temp = a[2];
a[2] = a[5];
a[5] = temp;
System.out.println(Arrays.toString(a));//遍历打印出数组a的元素
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L5ufJSaH-1638090027200)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211123171942322.png)]
2.4.4 冒泡排序😁
1.目标: 通过冒泡排序算法将数组中的元素进行升序或者降序排序
2.原理: 比较两个相邻的元素,将值大的元素交换至右端
3.思路:
依次比较相邻的两个数,将小数放在前面,大数放在后面。
😂即在第一趟:首先比较第1个和第将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,将小数放前,大数放后。至此第一趟结束,将最大的数放到了最后。
😁:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的
如此下去,重复以上过程,直至最终完成排序。
...
4.代码思路
用二重循环实现,外循环变量设为i,内循环变量设为j。假如有n个数需要进行排序,则外循每次进行比较的两个元素都是与内循环j有关的,它们可以分别用a[j]和a[j+1]标识
import java.util.Arrays;
public class Demo01 {
//冒泡排序
public static void main(String[] args) {
int[] a = {2,7,88,4,45,0}; //索引有0,1,2,3,4,5
int temp = 0;
for(int m = 0;m < a.length - 1;m++) {
//编写内循环,一趟
for (int i = 0; i < a.length - 1 - m; i++) {
if (a[i] > a[i + 1]) {
temp = a[i + 1];
a[i + 1] = a[i];
a[i] = temp;
}
}
}
System.out.println(Arrays.toString(a));//遍历打印出数组a的元素
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXRcNDEt-1638090027201)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211123171702354.png)]
2.5数组的扩容
Java数组扩容的原理
1)Java数组对象的大小是固定不变的,数组对象是不可扩容的。
2)利用数组复制方法可以变通的实现数组扩容。
3)System.arraycopy()可以复制数组。
4)Arrays.copyOf()可以简便的创建数组副本
5)创建数组副本的同时将数组长度增加就变通的实现了数组的扩容。
实际上此过程是废弃掉了旧的数组的地址,创建了新的数组,保留了原数组数据
案例:统计一个字符在字符串中的所有位置
import java.util.Arrays;
public class Demo01 {
public static void main(String[] args) {
char key = '是';//指定关键字
String str = "罗滔是大猪蹄子吗?是滴!";//定义字符串
System.out.println(str);
//赋值调用 调用count方法count(参数名,参数名),赋值给count数组
int[] count=count(str,key); //
System.out.println(Arrays.toString(count));//遍历打印出数组count的元素
}
//编写count方法
public static int[] count(String str ,char key) {
int[] count = {}; //定义一个空数组count
//for循环遍历比较 字符与key是否相等
for(int i = 0;i < str.length();i ++) {
char c = str.charAt(i); //把字符串第i个字符赋值给 c
//条件语句比较 c与key
if(c == key) {
//数组扩容,复制原数组,增加长度,
count = Arrays.copyOf(count, count.length + 1);
//把位置值i赋值给count数组
count[count.length - 1] = i;
}
}
return count;
}
}