day04_数组

java 文章 2022-07-20 10:02 0 全屏看文

数组

学习目标:

1. jvm内存图入门2. 一维数组的使用3. 二维数组的使用4. 数组的内存结构5. 数组中常见算法6. 数组中常见的异常

一、JVM内存图入门

java程序运行在jvm上,jvm内存主要分为五块,结构如下:

每块内存负责的职责如下:

  1. Java虚拟机栈(Java Virtual Machine Stacks):描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame),栈帧中存储着局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,会对应一个栈帧在虚拟机栈中入栈到出栈的过程。与程序计数器一样,Java虚拟机栈也是线程私有的。

局部变量和引用地址都是在栈内存中。

  1. 堆:此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。

  2. 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区存放的数据只有一份

  3. 本地方法栈(Native Method Stack):与Java虚拟机栈作用很相似,它们的区别在于虚拟机栈为虚拟机执行Java方法(即字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。例如调用本地c/c++的方法。

  4. 程序计数器:程序计数器是记录当前线程所执行的指令行数。

二、数组的概述

2.1 为什么需要数组

为什么要有数组?在开发中,我们常常需要存取很多相同类型的数据,用变量的方式需要定义很多变量,不方便管理。所以引入数组的概念,一次存取多个相同类型的数据。数组有两个一定,一个类型一定,一个是大小一定。

2.2 数组的基本概念

数组中存在着一些重要的概念,如下所示:

  • 数组名:数组的名称,数组内存的首地址
  • 下标名(索引):数组元素的序号索引,从0开始
  • 元素:数组中存放的内容
  • 长度 :数组的长度

三、一维数组的使用

3.1 数组的定义

一维数组的定义语法如下:

示例:

/*** 数组定义*/public class ArrayDemo1 {public static void main(String[] args) {//定义数组int[] arr1;String [] arr2;}}

3.2 数组的初始化

数组动态初始化语法:

示例:

/*** 数组定义,以及动态初始化*/public class ArrayDemo1 {public static void main(String[] args) {//定义数组int[] arr1;//数组动态初始化arr1 = new int[4];arr1[0] = 10;arr1[1] = 20;arr1[2] = 30;arr1[3] = 40;//取值System.out.println(arr1[0]);System.out.println(arr1[1]);System.out.println(arr1[2]);System.out.println(arr1[3]);}}

数组静态初始化语法:

示例:

/*数组的定义与静态初始化*/public class ArrayDemo2 {public static void main(String[] args) {//数组的声明// String[] arr = new String[]{"乔峰","段誉","杨过"};//简写String[] arr = {"乔峰","段誉","杨过"};//取值System.out.println(arr[0]);System.out.println(arr[1]);System.out.println(arr[2]);}}

3.3 数组元素的引用

引用数组元素内容需要注意以下几点:

  • 定义并用运算符new为之分配空间后,才可以引用数组中的每个元素;
  • 数组元素的引用方式:数组名[数组元素下标]
  • 数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
  • 数组元素下标从0开始;长度为n的数组合法下标取值范围: 0 —>n-1;如int a[]=new int[3]; 可引用的数组元素为a[0]、a[1]、a[2]
  • 每个数组都有一个属性length指明它的长度,例如:a.length 指明数组a的长 度(元素个数) 。数组一旦初始化,其长度是不可变的

示例1:数组元素的引用

/*** 数组元素的引用*/public class ArrayDemo3 {public static void main(String[] args) {//数组的定义int[] arr = {1,3,5,7,9};//查看数组的长度System.out.println(arr.length);// 查看数组内容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]);// 数组下标超过最大下标会越界,报异常ArrayIndexOutOfBoundsExceptionSystem.out.println(arr[5]);}}

示例2:数组的循环赋值与取值

/*** 数组的循环赋值与取值*/public class ArrayDemo4 {public static void main(String[] args) {//定义数组int[] arr = new int[5];//循环赋值for (int i = 0; i < arr.length; i++) {arr[i] = i*5;}//循环取值for (int i = 0; i < arr.length ; i++) {System.out.println(arr[i]);}}}

3.4 数组的注意事项

数组在使用过程中需要注意以下几点:

  • 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
  • 创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址。
  • 数组的长度一旦确定,就不能修改。
  • 数组的分类:
  • 按照维度:一维数组、二维数组、三维数组、…
  • 按照元素的数据类型分:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)

3.5 forEach的应用

forEach可以遍历数组或集合容器中的数据,语法如下:

示例:

/*** forEach遍历*/public class ArrayDemo5 {public static void main(String[] args) {//数组定义int[] arr = {1,3,5,7,9};//数组遍历for(int num : arr){System.out.println(num);}}}

3.6 数组元素默认值

当数组元素没有赋值时,是存在默认值的,默认值如下:

3.7 一维数组内存图

3.8 小结

  1. 数组的基本使用
  2. 数据类型可以是基本类型或引用类型
  3. 数组类型固定长度不变
  4. 数组默认值
  5. 数组的内存图

四、一维数组练习题

4.1 数组赋值练习

将班级所有同学名字录入到一个一维数组中 。

参考答案:

/*** 将班级所有同学名字录入到一个一维数组中*/public class ArrayTest1 {public static void main(String[] args) {//1.创建大小为5的String类型数组String[] names = new String[5];//2.创建扫描仪对象Scanner input = new Scanner(System.in);for(int i=0;i<names.length;i++) {//3.提示信息System.out.println("请输入第"+(i+1)+"个同学名字");//4.接收客户的输入信息String name = input.nextLine();//5.将名字存入数组中names[i] = name;}System.out.println("学生信息如下----------");for(int i=0;i<names.length;i++) {System.out.println(names[i]);}}}

4.2 求数组值练习

已知一个一维数组如下int[] arr = {1,3,11,5,7,9};求出所有元素的最大值,最小值,和值,平均值,并输出出来。

参考答案:

/*** 已知一个一维数组如下int[] arr = {1,3,11,5,7,9};求出所有元素的最大值,最小值,和值,平均值,并输出出来。*/public class ArrayTest2 {public static void main(String[] args) {//定义数组int[] arr = {1,3,11,5,7,9};//假设0下标是最大值 0下标是最小值int max = arr[0];int min = arr[0];int sum = 0;int avg = 0;//2.判断最大值和最小值for(int i=0;i<arr.length;i++) {if(max<arr[i]) {max = arr[i];}if(min>arr[i]) {min = arr[i];}sum+=arr[i];}avg = sum/arr.length;System.out.println("最大值"+max);System.out.println("最小值"+min);System.out.println("和是"+sum);System.out.println("平均值是"+avg);}}

4.3 数组的复制

已知数组int[] arr1 = {1,3,5,7,9} , int[] arr2; 将数组arr1的内容复制到数组arr2中

参考答案:

/*** 已知数组int[] arr1 = {1,3,5,7,9} , int[] arr2; 将数组arr1的内容复制到数组arr2中*/public class ArrayTest3 {public static void main(String[] args) {//1.定义数组int[] arr1,arr2;//2.初始化arr1 = new int[]{1,3,5,7,9};//3.打印数组arr1for(int i=0;i<arr1.length;i++) {System.out.println(arr1[i]);}//4.数组复制操作arr2 = new int[arr1.length];for(int i=0;i<arr1.length;i++) {arr2[i] = arr1[i];}}}

注意:切不可 arr2 = arr1 这不是复制数组内容,这是复制数组内存地址;arr2和arr1会指向同一块堆内存空间,改变arr2的同时也会改变arr1,改变arr1的同时也会改变arr2

4.4 数组的反转

已知一个数组如下String[] arr = new String[]{"AA","BB","CC","DD","EE"};,反转arr数组得到如下结果String[] arr = new String[]{"EE","DD","CC","BB","AA"};

参考答案:

/*** 已知一个数组如下String[] arr = new String[]{"AA","BB","CC","DD","EE"};,* 反转arr数组得到如下结果String[] arr = new String[]{"EE","DD","CC","BB","AA"};*/public class ArrayTest4 {public static void main(String[] args) {String[] arr = new String[]{"AA","BB","CC","DD","EE"};for(int i=0;i<arr.length/2;i++) {String str = arr[i];arr[i] = arr[arr.length-i-1];arr[arr.length-i-1] = str;}for(int i=0;i<arr.length;i++) {System.out.println(arr[i]);}}}

五、二维数组的使用

如果说可以把一维数组当成几何中的线性图形, 那么二维数组就相当于是一个表格。就像下面图一样:

对于二维数组的理解,我们可以理解为一维数组的数组。既数组0下标是一个一维数组,1下标又是一个一维数组数组...。

5.1 二维数组的动态初始化

二维数组存在两种定义方式,第一种定义方式如下:

  • int[][]arr = new int[4][3]; 定义了名为arr的二维数组,二维数组中有4个一维数组 每一个一维数组中有3个元素
  • 一维数组分别为arr[0], arr[1], arr[2] ,arr[3]
  • 给第一个一维数组0脚标位赋值为1写法是:arr[0][0] = 1;
  • arr 数组的长度 arr.length;
  • arr[0].length 代表arr数组的0下标一维数组的长度。

示例:

/*** 二维数组动态初始化1*/public class ArrayDemo7 {public static void main(String[] args) {//1.动态定义二维数组方式1/*两种理解方式	y x* 方式1 图形化理解 4 行 3 列 二维数组 int[][] arr = new int[4][3];* 方式2 有 4 个 一维数组 每个一维数组可以存放3个数*/int[][] arr = new int[4][3];//2.获得 4 个一维数组 arr[0] arr[1] arr[2] arr[3]//打印 第0个数组System.out.print(arr[0][0]);System.out.print(arr[0][1]);System.out.println(arr[0][2]);//打印 第1个数组System.out.print(arr[1][0]);System.out.print(arr[1][1]);System.out.println(arr[1][2]);//打印 第2个数组System.out.print(arr[2][0]);System.out.print(arr[2][1]);System.out.println(arr[2][2]);//打印 第3个数组System.out.print(arr[3][0]);System.out.print(arr[3][1]);System.out.println(arr[3][2]);//3.查看二维数组长度System.out.println(arr.length);//4. 查看二维数组0下标长度System.out.println(arr[0].length);//5. 查看二维数组1标长度System.out.println(arr[1].length);//6. 查看二维数组2下标长度System.out.println(arr[2].length);//6. 查看二维数组3下标长度System.out.println(arr[3].length);}}

第二种定义方式如下:

  • int[][] arr = new int[3][]; 二维数组中有3个一维数组。 每个一维数组都是默认初始化值null (注意:区别于格式1)
  • 可以对这个三个一维数组分别进行初始化 arr[0] = new int[3]; arr[1] = new int[2]; arr[2] = new int[1];

注: int[][]arr = new int[][3]; 这种写法是错误的

示例:

public class ArrayDemo8 {public static void main(String[] args) {//1.动态初始化2 /*** 图形化理解 定义 4行 图形 几列待定* 定义 4 个 一维数组 一维数组内容 没有初始化 */int[][] arr = new int[4][];//2.定义arr[0] 一维数arr[0] = new int[1];//3.定义arr[1] 一维数arr[1] = new int[2];//4.定义arr[2] 一维数arr[2] = new int[3];//5.定义arr[3] 一维数arr[3] = new int[4];//6.查看 数组长度System.out.println(arr.length);//7.查看0下标数组长度System.out.println(arr[0].length);//8.查看1下标数组长度System.out.println(arr[1].length);//9.查看2下标数组长度System.out.println(arr[2].length);//10.查看3下标数组长度System.out.println(arr[3].length);}}

5.2 二维数组的静态初始化

int[][] arr = new int[][]{{1},{1,2},{1,2,3}}; 定义一个名称为arr的二维数组,二维数组中有三个一维数组

每一个一维数组中具体元素也都已初始化 第一个一维数组 arr[0] = {1}; 第二个一维数组 arr[1] = {1,2}; 第三个一维数组 arr[2] = {1,2,3};

以上代码也可以简写为:int[][] arr = {{1},{1,2},{1,2,3}};

示例:

public class ArrayDemo9 {public static void main(String[] args) {//1.二维数组静态初始化int[][] arr = new int[][] {{1,1,1},{2,2,2},{3,3,3}};//2.简化int[][] arr2 = {{1},{2,2},{3,3,3}};}}

5.3 二维数组的遍历

二维数组循环赋值与遍历过程如下

public class ArrayDemo10 {public static void main(String[] args) {//1.矩形int[][] arr1 = new int[4][3];//2.循环赋值for(int y=0;y<arr1.length;y++) {for(int x=0;x<arr1[y].length;x++) {arr1[y][x] = 1;}}//3.循环遍历for(int y=0;y<arr1.length;y++) {for(int x=0;x<arr1[y].length;x++) {System.out.print(arr1[y][x]+"t");}System.out.println();}//2.直角三角形练习String[][] arr2 = new String[4][];//3.循环赋值for(int y=0;y<arr2.length;y++) {//4.定义一维数组arr2[y] = new String[y+1];for(int x=0;x<arr2[y].length;x++) {arr2[y][x] = "*";}}//4.打印二维数组for(int y=0;y<arr2.length;y++) {for(int x=0;x<arr2[y].length;x++) {System.out.print(arr2[y][x]);}System.out.println();}}}

5.4 小结

  1. 二维数组的动态初始化

  2. 二维数组的静态初始化

  3. 二维数组的遍历

六、二维数组练习题

6.1 杨辉三角

使用二维数组打印一个10行的杨辉三角

参考答案:

public class ArrayDemo11 {public static void main(String[] args) {//1.定义杨辉三角二维数组int[][] arr = new int[10][];//2.循环初始化 二维数组for(int y=0;y<arr.length;y++) {arr[y] = new int[y+1];//3.给每一行的第一个值和最后一个值赋值为 1arr[y][0] = arr[y][y] = 1;//4.给其他位置赋值//if(y>1) {for(int x=1;x<arr[y].length-1;x++) {arr[y][x] = arr[y-1][x-1]+arr[y-1][x];}//}}//3.遍历for(int y=0;y<arr.length;y++) {for(int x=0;x<arr[y].length;x++) {System.out.print(arr[y][x]+"t");}System.out.println();}}}

七、数组中常见算法

7.1 二分查找算法

已知数组int[] arr = {1,3,5,7,9,11,13};通过二分查找法查找数组中是否包含元素3,如果包含,元素3的下标是多少?注意(只有排好序的数组才能使用二分查找法)

分析过程如下:

另一种情况,当查找元素大于中间元素middle过程如下,这里以查找13为例

参考代码:

/*** 数组元素二分查找法,前提:已排序的数组*/public class BinarySearchArray {public static void main(String[] args) {//定义数组int[] arr = {1,3,5,7,9,11,13};// 定义查找目标数int target = 3;//开始下标int head = 0;//结束下标int end = arr.length-1;//标记是否找到目标元素boolean tag = true;//二分查找while(head<=end){int middle = (head+end)/2;if(arr[middle]==target){System.out.println("查找到元素:"+target+",下标:"+middle);tag = false;break;}else if(arr[middle]>target){end=middle-1;} else {head=middle+1;}}if(tag){System.out.println("没有找到目标元素"+target);}}}

7.2 数组的排序介绍(其余排序,由于算法过于复杂,后期统一讲)

排序:是计算机程序设计中的一项重要操作,是指对数组或集合中的元素进行按照大小或字母排序。

排序的算法有很多很多种,这里以冒泡排序、选择排序、插入排序为例进行讲解。

7.3 冒泡排序

冒泡排序的设计思想:从0下标开始比较相邻元素,通过交换下标元素位置,把大的元素放后面,小的元素放前面, 比较完一轮完成最后一个元素是最大的,以此类推,经过n轮比较完成排序的过程。

代码实现分析

  1. 一共比较几轮?
  2. 每轮比较几次?

参考答案

/*** 冒泡排序*/public class SortDemo1 {public static void main(String[] args) {//定义数组int[] arr = {1,11,9,3,7,5};//排序for(int i=0;i<arr.length-1;i++){ //如果有6个数,比较5轮就可以了,剩下一个一定是最小的// 因为是j和j+1比较所以j<arr.length-1,又因为每比较一次,就少比较一个数,// 所以j<arr.length-1-i;for(int j=0;j<arr.length-1-i;j++){// 比较相邻元素,交换相邻下标位置if(arr[j]>arr[j+1]){int t = arr[j];arr[j] = arr[j+1];arr[j+1] = t;}}}//打印数组for(int i=0;i<arr.length;i++){System.out.println(arr[i]);}}}

7.4 选择排序

选择排序的思路是:

  1. 将0下标和后面所有元素比,如果0下标不是最小元素,就交换位置,比较完一轮0下标就是最小的元素
  2. 以此类推将1下标元素和后面所有元素比较,如果1下标不是最小元素,就交换位置,比较完一轮1下标就第二小的元素
  3. 依此类推,完成整个数组的排序

代码实现分析

  1. 一共比较几轮?
  2. 每轮比较几次?

参考答案:

/*** 选择排序*/public class SortDemo2 {public static void main(String[] args) {//定义数组int[] arr = {1,11,9,3,7,5};//选择排序for(int i=0;i<arr.length-1;i++){ //如果6个数,比较5轮即可,剩下最后一个一定是最大的//让0下标i和后面所有下标比较;所以开始是i+1 结束到最大下标for(int j=i+1;j<arr.length;j++){if(arr[i]>arr[j]){int t = arr[i];arr[i] = arr[j];arr[j] = t;}}}//打印数组for(int i=0;i<arr.length;i++){System.out.println(arr[i]);}}}

7.5 插入排序

插入排序的设计思路是:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5

参考答案:

/*** 插入排序*/public class SortDemo3 {public static void main(String[] args) {//定义数组int[] arr = {1,11,9,3,7,5};//插入排序for(int index = 1;index<arr.length;index++){int t = arr[index];int leftIndex = index-1;while(leftIndex>=0 && arr[leftIndex]>t){arr[leftIndex+1] = arr[leftIndex];leftIndex--;}arr[leftIndex+1] = t;}//打印数组for(int i=0;i<arr.length;i++){System.out.println(arr[i]);}}}

7.6 小结

  1. 二分查找

  2. 冒泡排序

  3. 选择排序

  4. 插入排序

八、数组中常见的异常

数组中的常见异常:

  • 数组角标越界的异常:ArrayIndexOutOfBoundsExcetion
  • 空指针异常:NullPointerException

九、总结

  1. 一维数组的使用

  2. 二维数组的使用

  3. 一维数组的常见算法:查找和排序。

-EOF-