课程:宋红康 JAVA
数组的概述
数组(Array):就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名,以便统一管理他们,然后用编号区分他们,这个名字称为数组名,编号称为下标或索引(index)。组成数组的各个变量称为数组的元素(element)。数组中元素的个数称为数组的长度(length)。一维数组的使用
数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址。
数组的长度一旦确定,就不能修改。
我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
数组的分类:
- 按照维度:一维数组、二维数组、三维数组、…
- 按照元素的数据类型分:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)
一维数组的使用
声明和初始化
数组的声明
- 元素的数据类型[ ] 一维数组的名称;
- 元素的数据类型 一维数组名[ ];
初始化
静态初始化:立即赋值
数据类型[] 数组名 = {元素 1,元素 2,元素 3…};
数据类型[] 数组名 = new 数据类型[]{元素 1,元素 2,元素 3…};
数据类型[] 数组名;
数组名= new 数据类型[]{元素 1,元素 2,元素 3…};
动态初始化:规定长度,没赋值
数组存储的元素的数据类型[ ] 数组名字 = new 数组存储的元素的数据类型[长度];
数组存储的数据类型[ ] 数组名字;
数组名字 = new 数组存储的数据类型[长度];
元素的类型可以是任意的 Java 的数据类型。例如:int, String, Student 等
new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用 new 创建数组对象。
- 在 Java 中 new 的操作往往意味着在内存中开辟新的空间,这个内存空间分配在内存的堆区。
- 堆是用来存放由 new 创建的对象和数组,即动态申请的内存都存放在堆区。栈是用来存放在方法中定义的一些基本类型的变量和对象的引用变量。
数组有定长特性,长度一旦指定,不可更改。
一维数组的引用(调用):数组名[下标],如:array[0]
- 数组的角标(或索引)从 0 开始的,到数组的长度-1 结束。
- 任何获取数组长度:数组.length
如何遍历数组:for 循环语句
数组元素的默认初始化值
- 数组元素是整型:0;long 是 0L
- 数组元素是浮点型:0.0;float 是 0.0F
- 数组元素是 char 型:0 或’\u0000’(表现为空),而非’0’
- 数组元素是 boolean 型:false
- 引用类型:null
数组的内存解析
内存的简化结构
一维数组的内存解析
- 首先,放在 main 方法里面的都是局部变量,局部变量放在栈空间中
- new 的结构在堆空间中
- int[]arr:数组声明,在栈空间中加载一个叫 arr 的变量
- new int[]{1,2,3}:实际上做了两件事情
- 第一件事情:在堆空间中开辟了一个长度为 3 的数组的内存空间,并生成了一个首地址值(0x34ab)同时把地址值赋给栈空间的 arr ,这样栈空间的 arr 就可以通过地址值找到对应数据,并且初始化数组值(默认值 0)
- 第二件事情:把{1,2,3}的值分别赋值给对应位置的数组
- String[] arr1:在栈空间又声明一个 arr1
- new String[4]:在堆空间中开辟一个长度为 4 的数组内存,同时生成一个首地址值(0x12ab),然后赋给 arr1。开始时是默认值 null
- arr1[1]:通过地址值和角标找到堆空间中对应的值
- arr1[1] = “刘德华”:然后把“刘德华”这个值赋给对应的内存空间
- arr1[2] = “张学友”:同上
- new String[3]:在堆空间又创建了一个长度为 3 的数组的内存空间,并生成首地址值(0x5566)
- arr1 = new String[3]:又把地址值(0x5566)赋值给 arr1,于是 arr1 指向(0x12ab)的指针就没有了,而变成指向(0x5566)。最后(0x5566)的默认初始化值为 null
- sysout(arr1[1]):通过新的地址值和指针得到的输出结果是 null
- 而对于没有指针指向的数组,系统会根据垃圾回收机制(引用计数算法)在某个时间把该内存空间清空
- 当 main 方法执行完成之后,变量的作用域就没了,于是相继的 arr1 出栈,(0x5566)内存空间被回收;然后 arr 出栈,(0x34ab)内存空间被垃圾回收器回收
- 注意:对于 string 类型的数组,上面“刘德华”和“张学友”并不是直接放在堆空间中
多维数组的使用
对于二维数组的理解:对于二维数组的理解,我们可以看成是一维数组 array1 又作为另一个一维数组 array2 的元素而存在。其实,从数组底层的运行机制来看,其实没有多维数组。
二维数组的声明和初始化
声明:
- 元素的数据类型
[ ][ ]
二维数组的名称(推荐):int[][] arr
- 元素的数据类型
二维数组名[][]
:int arr[][]
- 元素的数据类型[]
二维数组名[]
:int[] arr[]
- 元素的数据类型
静态初始化
public class ArraysTest { public static void main(String[] args) { /* 元素的数据类型[][] 二维数组名 = new 元素的数据类型[][]{ {元素1,元素2,元素3 。。。}, {第二行的值列表}, ... {第n行的值列表} }; ------------------------------------------------------------ 元素的数据类型[][] 二维数组名; 二维数组名 = new 元素的数据类型[][]{ {元素1,元素2,元素3 。。。}, {第二行的值列表}, ... {第n行的值列表} }; */ int[][] arr; arr = new int[][]{ {1,2,3}, {4,5,6}, {7,8,9} }; /* 元素的数据类型[][] 二维数组的名称 = { {元素1,元素2,元素3 。。。}, {第二行的值列表}, ... {第n行的值列表} }; */ int[][] arr = { {1,2,3}, {4,5,6}, {7,8,9} }; } }
动态初始化
规则二维表:每一行的列数是相同的
public class ArraysTest { public static void main(String[] args) { //(1)确定行数和列数 // 元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n]; int[][]arr=new int[3][2]; // m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行 // n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格 //此时创建完数组,行数、列数确定,而且元素也都有默认值 //(2)再为元素赋新值 // 二维数组名[行下标][列下标] = 值; arr[0][0]=1; } }
不规则:每一行的列数可能不一样
public class ArraysTest { public static void main(String[] args) { //(1)先确定总行数 // 元素的数据类型[][] 二维数组名 = new 元素的数据类型[总行数][]; int[][]arr=new int[3][]; //此时只是确定了总行数,每一行里面现在是null //(2)再确定每一行的列数,创建每一行的一维数组 //二维数组名[行下标] = new 元素的数据类型[该行的总列数]; //此时已经new完的行的元素就有默认值了,没有new的行还是null //(3)再为元素赋值 //二维数组名[行下标][列下标] = 值; arr[0][5]=0; } }
如何调用数组的指定位置的元素:利用下角标:
arr1[0][1]
如何获取数组的长度:arr4.length
如何遍历数组:双重 for 循环
数组元素的默认初始化值
数组元素: 二维数组分为外层数组的元素,内层数组的元素
如:
int[][]arr = new int[4][3];
- 外层元素:
arr[0]
,arr[1]
等 - 内层元素:
arr[0][0]
,arr[1][2]
等
- 外层元素:
针对于初始化方式一:比如:
int[][] arr = new int[4][3];
外层元素的初始化值为:地址值
内层元素的初始化值为:与一维数组初始化情况相同
针对于初始化方式二:比如:
int[][] arr = new int[4][];
外层元素的初始化值为:null
内层元素的初始化值为:不能调用,否则报错。
数组的内存解析
int[][] arr1 = new int[4][]
:在栈空间中声明了一个 arr1 的变量,然后再堆空间中开辟了一个长度为 4 的二维数组的内存空间,生成首地址值(0x1234),然后把首地址值赋给栈空间的 arr1,通过地址值,栈空间的 arr1 就可以指向堆空间的(0x1234)。因为二维数组的每个位置都还没赋值,二维数组的每个元素其实是一个一维数组,是一个引用类型,所有每个位置的默认初始化为 nullarr1[1] = new int[]{1,2,3}
:在堆空间里面开辟了长度为 3 的一维数组的内存空间,生成首地址值(0x7788),然后讲首地址值赋给 arr1[1],arr1[1]就可以通过首地址值找到对应数据。arr1[1]是一个一维数组,对应的元素就是一个 int 的整数类型的数据,因此默认值是 0,然后把{1,2,3}赋给各个位置上arr1[2] = new int[4]
:开辟一个长度为 4 的一维数组空间(0x6677),并赋值arr1[2][1] = 30
:通过(0x1234)找到外层数组,然后通过arr1[2]
找到对应的(0x6677)的一维数组,通过arr1[2][1]
找到(0x6677)的第 2 个元素,并赋值 30。
数组中涉及的常见算法
数组元素的赋值(杨辉三角、回形数等)

/*
* 使用二维数组打印一个 10 行杨辉三角。
【提示】
1. 第一行有 1 个元素, 第 n 行有 n 个元素
2. 每一行的第一个元素和最后一个元素都是 1
3. 从第三行开始, 对于非第一个元素和最后一个元素的元素。即:
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
*
*/
public class YangHuiTest {
public static void main(String[] args) {
//1.声明并初始化二维数组
int[][] yangHui = new int[10][];
//2.给数组的元素赋值
for(int i = 0;i < yangHui.length;i++){
yangHui[i] = new int[i + 1];
//2.1 给首末元素赋值
yangHui[i][0] = yangHui[i][i] = 1;
//2.2 给每行的非首末元素赋值
//if(i > 1){
for(int j = 1;j < yangHui[i].length - 1;j++){
yangHui[i][j] = yangHui[i-1][j-1] + yangHui[i-1][j];
}
//}
}
//3.遍历二维数组
for(int i = 0;i < yangHui.length;i++){
for(int j = 0;j < yangHui[i].length;j++){
System.out.print(yangHui[i][j] + " ");
}
System.out.println();
}
}
}
求数值型数组中元素的最大值、最小值、平均数、总和等
/*
* 算法的考查:求数值型数组中元素的最大值、最小值、平均数、总和等
*
* 定义一个int型的一维数组,包含10个元素,分别赋一些随机整数,
* 然后求出所有元素的最大值,最小值,和值,平均值,并输出出来。
* 要求:所有随机数都是两位数。
*
* [10,99]
* 公式:(int)(Math.random() * (99 - 10 + 1) + 10)
*
*/
public class ArrayTest1 {
public static void main(String[] args) {
int[] arr = new int[10];
for(int i = 0;i < arr.length;i++){
arr[i] = (int)(Math.random() * (99 - 10 + 1) + 10);
}
//遍历
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + "\t");
}
System.out.println();
//求数组元素的最大值
int maxValue = arr[0];
for(int i = 1;i < arr.length;i++){
if(maxValue < arr[i]){
maxValue = arr[i];
}
}
System.out.println("最大值为:" + maxValue);
//求数组元素的最小值
int minValue = arr[0];
for(int i = 1;i < arr.length;i++){
if(minValue > arr[i]){
minValue = arr[i];
}
}
System.out.println("最小值为:" + minValue);
//求数组元素的总和
int sum = 0;
for(int i = 0;i < arr.length;i++){
sum += arr[i];
}
System.out.println("总和为:" + sum);
//求数组元素的平均数
int avgValue = sum / arr.length;
System.out.println("平均数为:" + avgValue);
}
}
数组的复制、反转、查找(线性查找、二分法查找)
/*
使用简单数组
(1)创建一个名为ArrayExer2的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
(2)使用大括号{},把array1初始化为8个素数:2,3,5,7,11,13,17,19。
(3)显示array1的内容。
(4)赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)。打印出array1。
思考:array1和array2是什么关系?array1和array2地址值相同,都指向了堆空间的唯一的一个数组实体。
拓展:修改题目,实现array2对array1数组的复制
*/
public class ArrayExer2 {
public static void main(String[] args) { //alt + /
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
//显示array1的内容
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
//赋值array2变量等于array1
//不能称作数组的复制。只是把array1的地址值给了array2
array2 = array1;
//修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)
for(int i = 0;i < array2.length;i++){
if(i % 2 == 0){
array2[i] = i;
}
}
System.out.println();
//打印出array1
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
}
}
/*
* 使用简单数组
* 拓展:修改题目,实现array2对array1数组的复制
*/
public class ArrayExer3 {
public static void main(String[] args) { //alt + /
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
//显示array1的内容
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
//数组的复制:
array2 = new int[array1.length];
for(int i = 0;i < array2.length;i++){
array2[i] = array1[i];
}
//修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)
for(int i = 0;i < array2.length;i++){
if(i % 2 == 0){
array2[i] = i;
}
}
System.out.println();
//打印出array1
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
}
}
/*
* 算法的考查:数组的复制、反转、查找(线性查找、二分法查找)
*/
public class ArrayTest2 {
public static void main(String[] args) {
String[] arr = new String[]{"JJ","DD","MM","BB","GG","AA"};
//数组的复制(区别于数组变量的赋值:arr1 = arr)
String[] arr1 = new String[arr.length];
for(int i = 0;i < arr1.length;i++){
arr1[i] = arr[i];
}
//数组的反转
//方法一:
// 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,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++){
System.out.print(arr[i] + "\t");
}
System.out.println();
//查找(或搜索)
//线性查找:
String dest = "BB";
dest = "CC";
boolean isFlag = true;
for(int i = 0;i < arr.length;i++){
if(dest.equals(arr[i])){
System.out.println("找到了指定的元素,位置为:" + i);
isFlag = false;
break;
}
}
if(isFlag){
System.out.println("很遗憾,没有找到的啦!");
}
//二分法查找:(熟悉)
//前提:所要查找的数组必须有序。
int[] arr2 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int dest1 = -34;
dest1 = 35;
int head = 0;//初始的首索引
int end = arr2.length - 1;//初始的末索引
boolean isFlag1 = true;
while(head <= end){
int middle = (head + end)/2;
if(dest1 == arr2[middle]){
System.out.println("找到了指定的元素,位置为:" + middle);
isFlag1 = false;
break;
}else if(arr2[middle] > dest1){
end = middle - 1;
}else{//arr2[middle] < dest1
head = middle + 1;
}
}
if(isFlag1){
System.out.println("很遗憾,没有找到的啦!");
}
}
}
数组元素的排序算法
冒泡排序:
- 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止。
/* * 数组的冒泡排序的实现 * */ public class BubbleSortTest { public static void main(String[] args) { int[] arr = new int[]{43,32,76,-98,0,64,33,-21,32,99}; //冒泡排序 for(int i = 0;i < arr.length - 1;i++){ 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; } } } for(int i = 0;i < arr.length;i++){ System.out.print(arr[i] + "\t"); } } }
快速排序:
- 从数列中挑出一个元素,称为“基准” ( pivot)。
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准。值大的摆在基准的后面(相同的数可以到任一-边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition) 操作。
- 递归地(recursive) 把小于基准值元素的子数列和大于基准值元素的子数列排序。
- 递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一-个元素摆到它最后的位置去。
public class QuickSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); return quickSort(arr, 0, arr.length - 1); } private int[] quickSort(int[] arr, int left, int right) { if (left < right) { int partitionIndex = partition(arr, left, right); quickSort(arr, left, partitionIndex - 1); quickSort(arr, partitionIndex + 1, right); } return arr; } private int partition(int[] arr, int left, int right) { // 设定基准值(pivot) int pivot = left; int index = pivot + 1; for (int i = index; i <= right; i++) { if (arr[i] < arr[pivot]) {// 找到 小于arr[pivot] 的index swap(arr, i, index);// 把小于arr[pivot]的数放在最左边 index++; // 记录小于arr[pivot]的数字 } } // index - 1是遍历一次找到的最后一个小于arr[pivot]的数 swap(arr, pivot, index - 1); return index - 1; } private void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } }
Arrays 工具类的使用
public class ArraysTest {
public static void main(String[] args) {
//1.boolean equals(int[] a,int[] b):判断两个数组是否相等。比较的是两个数组的值
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,3,2,4};
boolean isEquals = Arrays.equals(arr1, arr2);
System.out.println(isEquals);//false
//2.String toString(int[] a):输出数组信息。
System.out.println(Arrays.toString(arr1));//[1, 2, 3, 4]
//3.void fill(int[] a,int val):将指定值填充到数组之中。
Arrays.fill(arr1,10);
System.out.println(Arrays.toString(arr1));//[10, 10, 10, 10]
//4.void sort(int[] a):对数组进行排序。
Arrays.sort(arr2);//[1, 2, 3, 4]
System.out.println(Arrays.toString(arr2));
//5.int binarySearch(int[] a,int key)
int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
// 对排序后的数组进行二分法检索指定的指
int index = Arrays.binarySearch(arr3, 210);
if(index >= 0){
System.out.println(index);//8
}else{
System.out.println("未找到");
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com