4.1 数组概念
4.1.1 数组的概念
数组概念: 数组就是用于存储数据的长度固定的容器,保证多个数据的数据类型要一致。
数组的定义:
所谓数组(array),就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名,以便统一管理他们,然后用编号区分他们,这个名字称为数组名,编号称为下标或索引(index)。组成数组的各个变量称为数组的元素(element)。数组中元素的个数称为数组的长度(length)。
4.1.2 数组的分类
1、按照维度分:
-
一维数组:存储一组数据
-
二维数组:存储多组数据,相当于二维表,一行代表一组数据,这是这里的二维表每一行长度不要求一样。
2、按照元素类型分:
-
基本数据类型的元素:存储数据值
-
引用数据类型的元素:存储对象(本质上存储对象的首地址)(这个在面向对象部分讲解)
注意:无论数组的元素是基本数据类型还是引用数据类型,数组本身都是引用数据类型。
4.2 一维数组的声明与使用
4.2.1 一维数组的声明
一维数组的声明/定义格式:
//推荐
元素的数据类型[ ] 数组名;//不推荐
元素的数据类型 数组名[ ];
数组的声明,就是要确定:
(1)数组的维度:在Java中数组的标点符号是[ ],[ ]表示一维,[ ][ ]表示二维
(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如:int, String等
(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。
package com.huohua.day0408.code;
public class ArrayCase2 {
public static void main(String[] args) {
//数组的声明
int ageArr1[];
//推荐使用下面这种
//存储一组整形的数据
int[] ageArr2;
//存储一组浮点型的数据
float[] score;
//存储一组字符型数据
char[] hobby;
//要使用之前你必须要初始化
//System.out.println(ageArr1);
}
}
4.2.2 一维数组的静态初始化
-
什么是初始化?
-
初始化就是确定数组元素的总个数(即数组的长度)和元素的值
-
-
什么是静态初始化?
-
静态初始化就是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。
-
-
一维数组静态初始化格式1:
数据类型[ ] 数组名 = {元素1,元素2,元素3...};//必须在一个语句中完成,不能分开两个语句写
- 一维数组静态初始化格式2:
数据类型[ ] 数组名 = new 数据类型[ ]{元素1,元素2,元素3...};
或
数据类型[ ] 数组名;
数组名 = new 数据类型[ ]{元素1,元素2,元素3...};
例如:
package com.huohua.day0408.code;
public class ArrayCase3 {
public static void main(String[] args) {
//声明 静态初始化
//第一种
// int[] ageArr = {1, 2, 3, 4, 5};
// int[] ageArr1;//不能先声明再赋值
// ageArr1 = {1,2,3,4,5};
// float[] score = {95f,45,96f,87};
// char[] charVar = {'a','b','c',35};
//第二种 静态数据初始化
//引用数据类型
// int[] ageArr = new int[]{1, 2, 3, 4, 5};
float[] score = new float[]{95f, 45, 96f, 87};
int[] ageArr;//可以先声明再赋值
ageArr =new int[]{1,2};
}
}
4.2.3 一维数组的使用
-
如何获取数组的元素总个数,即数组的长度
数组的长度属性: 每个数组都具有长度,而且是固定的,Java中赋予了数组的一个属性,可以获取到数组的长度,语句为:数组名.length
,属性length的执行结果是数组的长度,int类型结果。
数组名.length
-
如何表示数组中的一个元素?
每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引(index)或下标,可以通过数组的索引/下标访问到数组中的元素。
数组名[ 索引/下标 ]
-
数组的下标范围?
Java中数组的下标从[0]开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1]
-
一维数组的使用
package com.huohua.day0408.code;
public class ArrayCase4 {
public static void main(String[] args) {
// 数组的长度
int[] ageArr = {1,2,3,4,5};
// 数组的长度
System.out.println(ageArr.length);
// 下标 从0 开始 [index]
System.out.println(ageArr[0]);
System.out.println(ageArr[4]);
// 蓝色的字是代码的错误 下标越界超出数组的范围
// (ArrayCase4.java:13)
System.out.println(ageArr[5]);
}
}
4.2.4 一维数组遍历
数组遍历: 就是将数组中的每个元素分别获取出来,就是遍历。遍历也是数组操作中的基石。for循环与数组的遍历是绝配。
package com.huohua.day0408.code;
public class ArrayCase5 {
public static void main(String[] args) {
float[] score = {95f, 45, 96f, 87};
//0 ~ score.length-1
for (int i = 0; i < score.length; i++) {
System.out.println(score[i]);
}
char[] charVar = {'a','b','c',35};
for (int i = 0; i <charVar.length ; i++) {
System.out.println(charVar[i]);
}
boolean[] bool = {true,false,false,true};
for (int i = 0; i <bool.length ; i++) {
System.out.println(bool[i]);
}
int[] intNum = {1,2,3,4,5};
for (int i = 0; i <intNum.length ; i++) {
System.out.println(intNum[i]);
}
}
}
4.2.5 一维数组的动态初始化
-
什么是动态初始化?
动态初始化就是先确定元素的个数(即数组的长度),而元素此时只是默认值,并不是真正的数据。元素真正的数据需要后续单独一个一个赋值。
-
格式:
数组存储的元素的数据类型[ ] 数组名 = new 数组存储的元素的数据类型[ 长度 ];
或
数组存储的数据类型[ ] 数组名;
数组名字 = new 数组存储的数据类型[ 长度 ];
-
new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用new创建数组对象。
-
[ 长度 ]:数组的长度,表示数组容器中可以存储多少个元素。
-
注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。
package com.huohua.day0408.code;
import java.util.Scanner;
public class ArrayCase6 {
public static void main(String[] args) {
//动态初始化时,已经给了初始值
int[] arr = new int[5];
// for (int i = 0; i < arr.length; i++) {
// System.out.println(arr[i]);
// }
// for (int i = 0; i < arr.length; i++) {
// arr[i] = 2 * i;
// System.out.println(arr[i]);
// }
Scanner scan = new Scanner(System.in);
for (int i = 0; i < arr.length; i++) {
System.out.println("请输入第" + (i + 1) + "个值");
arr[i] = scan.nextInt();
}
String outArr = "[";
for (int i = 0; i < arr.length; i++) {
if (i == 0) {
outArr += arr[i];
} else {
outArr += "," + arr[i];
}
}
outArr += "]";
System.out.println(outArr);
}
}
4.2.6 数组元素的默认值
当我们使用动态初始化方式创建数组时,元素只是默认值。
4.3 一维数组的内存分析
4.3.1 内存概述
内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来。我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。
Java虚拟机要运行程序,必须要对内存进行空间的分配和管理。
4.3.2 Java虚拟机的内存划分
为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
区域名称 | 作用 |
---|---|
程序计数器 | 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址 |
本地方法栈 | 当程序中调用了native的本地方法时,本地方法执行期间的内存区域 |
方法区 | 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 |
堆内存 | 存储对象(包括数组对象),new来创建的,都存储在堆内存。 |
虚拟机栈 | 用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。 |
4.4.3 一维数组在内存中的存储
1、一个一维数组内存图
package com.huohua.day0408.code;
public class ArrayCase7 {
public static void main(String[] args) {
int[] arr1=new int[5];
System.out.println(arr1);//[I@1b6d3586
}
}
思考:打印arr为什么是[I@1b6d3586,它是数组的地址吗?
答:它不是数组的地址。
问?不是说arr中存储的是数组对象的首地址吗?
答:arr中存储的是数组的首地址,但是因为数组是引用数据类型,打印arr时,会自动调用arr数组对象的toString()方法,该方法默认实现的是对象类型名@该对象的hashCode()值的十六进制值。
问?对象的hashCode值是否就是对象内存地址?
答:不一定,因为这个和不同品牌的JVM产品的具体实现有关。例如:Oracle的OpenJDK中给出了5种实现,其中有一种是直接返回对象的内存地址,但是OpenJDK默认没有选择这种方式。
2、数组下标为什么是0开始
因为第一个元素距离数组首地址间隔0个单元格。
3、两个一维数组内存图
两个数组独立
package com.huohua.day0408.code;
public class ArrayCase7 {
public static void main(String[] args) {
int[] arr1=new int[5];
char[] arr2 = new char[5];
boolean[] arr3 = new boolean[5];
float[] arr4 = new float[5];
String[] arr5 = new String[5];
int[] arr6 = new int[10];
System.out.println(arr1);//[I@1b6d3586
System.out.println(arr2);//
System.out.println(arr3);//[Z@4554617c
System.out.println(arr4);//[F@74a14482
System.out.println(arr5);//[Ljava.lang.String;@1540e19d
System.out.println(arr6);//[I@677327b6
}
}
4、两个变量指向一个一维数组
两个数组变量本质上代表同一个数组。
public static void main(String[] args) {
// 定义数组,存储3个元素
int[] arr = new int[3];
//数组索引进行赋值
arr[0] = 5;
arr[1] = 6;
arr[2] = 7;
//输出3个索引上的元素值
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
//定义数组变量arr2,将arr的地址赋值给arr2
int[] arr2 = arr;
arr2[1] = 9;
System.out.println(arr[1]);
}
4.4 一维数组常见算法
4.4.1 数组统计:求和,求均值,统计偶数个数等。
package com.huohua.day0408.code;
public class ArrayCase15 {
public static void main(String[] args) {
//求总和以及均值、总乘积、统计偶数个数
int[] arr = {1, 2, 3, 4, 5};
//求总和
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
System.out.println("总和:"+sum);
//均值
System.out.println("均值:"+(double)sum/arr.length);
}
}
package com.huohua.day0408.code;
public class ArrayCase16 {
public static void main(String[] args) {
//求总和以及均值、总乘积、统计偶数个数
int[] arr = {1, 2, 3, 4, 5};
//总乘积
int num=1;
for (int i = 0; i <arr.length ; i++) {
num *=arr[i];
}
System.out.println("总乘积:"+num);
}
}
package com.huohua.day0408.code;
public class ArrayCase17 {
public static void main(String[] args) {
//求总和以及均值、总乘积、统计偶数个数
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
//偶数个数
int count = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] % 2 == 0) {
count++;
}
}
System.out.println("偶数个数:" + count);
}
}
4.4.2 数组找最值
1、找最大值/最小值
思路:
(1)先假设第一个元素最大/最小
(2)然后用max/min与后面的元素一一比较
package com.huohua.day0408.code;
public class ArrayCase11 {
public static void main(String[] args) {
int[] arr = new int[]{14, 26, 32, 13, 25};
//求最大值和最小值
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
if (min > arr[i]) {
min = arr[i];
}
}
System.out.println("最大值"+max);
System.out.println("最小值"+min);
}
}
2、找最值及其第一次出现的下标
思路:
(1)先假设第一个元素最大/最小
(2)用max/min变量表示最大/小值,用max/min与后面的元素一一比较
(3)用index时刻记录目前比对的最大/小的下标
package com.huohua.day0408.code;
public class ArrayCase13 {
public static void main(String[] args) {
int[] arr = new int[]{14, 26, 32, 13, 25};
//求最大值和最小值
int maxIndex = 0;
int minIndex = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[maxIndex] < arr[i]) {
maxIndex = i;
}
if (arr[minIndex] > arr[i]) {
minIndex = i;
}
}
System.out.println("最大值下标"+maxIndex);
System.out.println("最小值下标"+minIndex);
}
}
4.4.3 数组的元素查找
1、顺序查找
顺序查找:挨个查看
要求:对数组元素的顺序没要求
package com.huohua.day0408.code;
import java.util.Scanner;
public class ArrayCaseOrderSeach19 {
public static void main(String[] args) {
//顺序查找
int[] arr = {3, 6, 7, 1, 2, 10, 21, 4};
Scanner scan = new Scanner(System.in);
System.out.println("请输入你要找的值");
int value = scan.nextInt();
int index = -1;
for (int i = 0; i < arr.length; i++) {
if (value == arr[i]) {
index = i;
break;
}
}
if (index == -1) {
System.out.println(value + "在数组中不存在");
} else {
System.out.println(value + "第一次出现的索引下标是:" + index);
System.out.println("数组的总长度位" + arr.length);
}
}
}
2、二分查找
package com.huohua.day0408.code;
import java.util.Scanner;
public class ArrayCaseBinatySeach18 {
public static void main(String[] args) {
//二分查找(折半查找) 必须是有序数组
//①
int[] arr = {1, 2, 3, 4, 6, 7, 10, 21};
Scanner scan = new Scanner(System.in);
System.out.println("请输入你要找的值");
//②
int value = scan.nextInt();
//③设置起始的下标位
int left = 0;
//④结束的下标位
int right = arr.length - 1;
//⑤目标元素的index值
int index = -1;
while (left <= right) {
//⑥
int mid = (left + right) / 2;
//判断比较
if (arr[mid] == value) {
index = mid;
break;
}
if (arr[mid] > value) {
right = mid - 1;
}
if (arr[mid] < value) {
left = mid + 1;
}
}
if (index == -1) {
System.out.println("数组中不存在你要找的值");
} else {
System.out.println("你要找的值" + value + "的索引下标为" + index);
}
}
}
4.4.4 数组元素排序
1、排序算法概述
数组的排序算法很多,实现方式各不相同,时间复杂度、空间复杂度、稳定性也各不相同:
2、直接选择排序
package com.huohua.day0408.code;
/*
选择排序
从小到大
目标元素设定位置,按照目标的本来元素和目标后面的元素进行比较,直到尾部
当比较一轮后,记录最小值的索引与目标位置的元素进行交换
*/
import java.util.Arrays;
public class SelectOrder20 {
public static void main(String[] args) {
int[] arr = {6, 3, 12, 4, 8};
//按照循环设定目标位置
for (int i = 0; i < arr.length; i++) {
//目标后面的元素进行一一比对
int min = arr[i];
int index = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < min) {
min = arr[j];
index = j;
}
}
//交换数值
if (index != i) {
//中间变量进行交换
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
}
System.out.println(Arrays.toString(arr));
}
}
3、冒泡排序
Java中的经典算法之冒泡排序(Bubble Sort)
原理:比较两个相邻的元素,将值大的元素交换至右端。
思路:依次比较相邻的两个数,将小数放到前面,大数放到后面。
即第一趟,首先比较第1个和第2个元素,将小数放到前面,大数放到后面。
然后比较第2个和第3个元素,将小数放到前面,大数放到后面。
如此继续,直到比较最后两个数,将小数放到前面,大数放到后面。
重复第一趟步骤,直至全部排序完成。
package com.huohua.day0408.code;
/*
冒泡排序
两两交换,每一轮比较使最大沉到最右边
*/
import java.util.Arrays;
public class BubbleOrder21 {
public static void main(String[] args) {
// 趟数
int tang = 0;
//每趟比较的次数
int count = 0;
int[] arr = {1,2,3,4,5,7};
boolean flag = true;
//趟数 每趟产生一个值在最右端
for (int i = 0; i < arr.length - 1; i++) {
//比较的次数 由于每次都减少一个比较 所有减去i
for (int j = 0; j < arr.length - i - 1; j++) {
//如果前面的数大了,那么交换数据
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = false;
}
count++;
}
//if (flag) {
// break;
//}
System.out.println("次数:" + count);
tang++;
}
System.out.println("趟数:" + tang);
System.out.println(Arrays.toString(arr));
}
}
4、冒泡排序优化
package com.huohua.day0408.code;
/*
冒泡排序
两两交换,每一轮比较使最大沉到最右边
*/
import java.util.Arrays;
public class BubbleOrder21 {
public static void main(String[] args) {
// 趟数
int tang = 0;
//每趟比较的次数
int count = 0;
int[] arr = {1,2,3,4,5,7};
boolean flag = true;
//趟数 每趟产生一个值在最右端
for (int i = 0; i < arr.length - 1; i++) {
//比较的次数 由于每次都减少一个比较 所有减去i
for (int j = 0; j < arr.length - i - 1; j++) {
//如果前面的数大了,那么交换数据
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = false;
}
count++;
}
if (flag) {
break;
}
System.out.println("次数:" + count);
tang++;
}
System.out.println("趟数:" + tang);
System.out.println(Arrays.toString(arr));
}
}
4.5 二维数组
数组中的元素也是数组
4.5.1 二维数组的静态初始化
package com.huohua.day0409;
public class SecArray1 {
public static void main(String[] args) {
int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[]{1, 2, 3, 4, 5};
// 二维数组初始化
//静态初始化
//方法一
int[][] secArr1 = {{1, 2}, {3, 4, 5}, {6, 7, 8, 9, 0}};
//方法二
int[][] secArr2 = new int[][]{{1, 2}, {3, 4}, {5, 6}};
int[][] secArr3;
secArr3 = new int[][]{{1, 2}, {3, 4}, {5, 6}};
}
}
4.5.2 获取二维数组长度
格式:数组名.length
int[][] secArr1 = {{1, 2}, {3, 4, 5}, {6, 7, 8, 9, 0}};
//二维数组的长度
System.out.println(secArr1.length);
//数组中数组的长度
for (int i = 0; i < secArr1.length; i++) {
System.out.println(secArr1[i].length);
}
4.5.3 获取二维数组中的元素
格式:数组名[i][j]
i:代表的是一维数组在二维数组中的索引位置
j:代表的是元素在一维数组中的索引位置
int[][] secArr1 = {{1, 2}, {3, 4, 5}, {6, 7, 8, 9, 0}};
//二维数组中的数据 数组名【】【】
//三行 第三个
System.out.println("第三行第三个数据" + secArr1[2][2]);
4.5.4 二维数组的动态初始化
1.概述:数组中的套多个数组
2.定义格式
a.动态初始化
数据类型[ ][ ] 数组名 = new 数据类型[m][n]
数据类型 数组名[ ][ ] = new 数据类型[m][n]
数据类型[ ] 数组名[ ] = new 数据类型[m][n]
m:代表的是二维数组的长度
n:代表的是二维数组中每一个一维数组的长度
package com.huohua.day0409;
import java.util.Scanner;
public class SecArrayitra3 {
public static void main(String[] args) {
int[][] secArr = new int[5][3];
Scanner scan = new Scanner(System.in);
for (int i = 0; i <secArr.length ; i++) {
for (int j = 0; j <secArr[i].length ; j++) {
// System.out.println("请输入"+(i+1)+"行"+(j+1)+"列的值");
// secArr[i][j]=scan.nextInt();
secArr[i][j]=i*2;
}
}
for (int i = 0; i <secArr.length ; i++) {
//然后根据里层数组的长度分别再次遍历数组[][]
for (int j = 0;j<secArr[i].length;j++){
System.out.print(secArr[i][j]+" ");
}
System.out.println();
}
}
}