12. Java 数组
一、什么是数组
数组可以理解成一个包含相同类型的有序数字集合 也称储存一组数据的空间
数组属于引用数据类型
int[] a = {1,2,3,4,5};
集合内的数据称为元素 并按顺序排列
每个元素对应一个元素索引 相当于每个数据的编号
索引的排序是从0开始
二、数组声明定义的语法
基本语法与变量的声明格式类似
格式:数据类型 变量名字 = 变量值
数组声明的两种方式:
首选格式(一般使用风格)
String[] args;
字符串数组类型 名字为 args
C/C++风格(方便C/C++编写者阅读和使用)
double = number[];
数组如何定义:使用new操作符
数组的两种定义方式:
第一种 动态初始化
构建方法为创建一个int类型引用赋值给number变量 元素的值可以更改
int[] number = new int[2];//单独定义前必须先声明 [2]内为数组的长度
int[0] = 1;//赋值给元素索引0 数组的第一个数据
没有被赋值的元素为默认值 称为隐式初始化
第二种 静态初始化
把包含元素的一组数据赋值给int数据类型的number变量
int[] number = {1,2,3,4,5,6,7,8,9,10};//声明和定义用一段语句来完成
静态初始化后的元素被赋予的值不能改变
定义数组的长度:
int[] number = int[10];//声明并定义一个数组长度为10
循环的范围是数组索引最后一个为止 元素下标是从0到9
for (int i = 0,i < number.length,i++){};
给某个索引赋值时不能大于数组的长度 否则会导致数组下标越界异常
int[9] = 1;//这个一个错误的语句
总结数组的四个特点:
-
数组的长度在创建后就不能改变。
-
数组内的元素必须是同种类型的数据。
-
数组内的元素可以是基本类型也可以是引用类型
-
数组属于引用类型数据,所以可以把数组看成一个对象,数组内的元素可以看出是成员变量。数组对象是被储存在堆里的。
三、数组的使用
使用数组的三种方式:
使用普通for循环和增强for循环 打印每个元素
把数组数据类型的参数传递给void无返回值的函数
把数组参数传递给有返回值的函数
1. 使用for循环和增强for循环的案例
for循环:
案例:打印数组内每个元素:
由于每一个数组含有多个元素 定义数组的索引为for循环的变量
增强for循环:
案例:打印数组内每个元素
增强for循环的变量用来声明数组的元素
表达式为要访问的数组
2. 定义数组变量传递给空返回值的函数
案例:选出数组中所有奇数
将数组变量传递给函数输出
3. 定义数组参数给有返回值的函数并输出返回值
案例:反转数组中的元素
编程原理:
-
首先把传递参数ars数组的长度赋值给result 用于之后返回实参
-
创建含有2个变量的循环结构
i变量为ars数组的下标 初始值为0 是ars数组的第一个元素 j变量为result数组的下标 初始值result数组长度为4 是result数组的最后一个元素 i变量的更新方式是自增 j变量的更新方式是自减
循环的表达公式就为:ars数组的第一个元素将result数组的最后一个元素的值覆盖掉
如:
result[4] = ars[0] result数组:{0,0,0,0,1}
result[3] = ars[1] result数组:{0,0,0,2,1}
result[2] = ars[2] result数组:{0,0,3,2,1}
result[1] = ars[3] result数组:{0,4,3,2,1}
result[0] = ars[4] result数组:{5,4,3,2,1}
-
result数组为{5,4,3,2,1}返回到ars形参中
-
使用引用的方式调用反转函数并循环输出
四、多维数组
多维数组实质意义上就是数组的嵌套使用
比如二维数组中的元素不是数字而是另一个数组
创建一个多维数组:
int[][] = new int[5][2];
第一个中括号表示最外层的数组长度为5
第二个中括号表示里面的数组长度为2
展开的数组如下:
int[][] arrays = {{1, 2},{2, 3},{3, 4},{4, 5},{5, 6}};
{1, 2}可以看成是最外层数组的一个元素
简易分析图:
二维数组嵌套两层 n维数组嵌套n层 以此类推
拓展:稀疏数组
定义:若一个数组中含有大量0元素,相同元素。
可以使用稀疏数组进行压缩,将不为0的元素抽取出来储存,从而达到节约空间的作用。
稀疏数组是一种数据结构。
使用案例:
多维数组压缩和读取
左边是原始数组 右边为压缩后的稀疏数组也叫稀疏矩阵
稀疏矩阵的参数:
-
row 表示数组行下标 第一行的row表示整个数组总共有多少行
-
col 表示数组列下标 第一行的col表示整个数组总共有多少列
-
value 表示每个坐标对应的元素 第一行的value表示整个数组总共有多少个元素
以下是原数组的代码:
//建立一个6行7列的空数组
int[][] arr = new int[6][7];
//定义有效元素8个
arr[0][3] = 22;
arr[0][6] = 15;
arr[1][1] = 11;
arr[1][5] = 17;
arr[2][3] = -6;
arr[3][5] = 39;
arr[4][0] = 91;
arr[5][2] = 28;
//外层循环表示行
for (int[] a : arr) {
//内层循环表示列
for (int b : a) {
System.out.print(b + "\t");
}
System.out.println();
}
遍历完所有元素后 就需要把所有的无效元素剔除掉
然后需要再遍历一次数组找到那些有效的值
//创建变量用来存储有效元素的个数
int count = 0;
//再遍历一遍数组
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
//判断元素非0的存入count
if (arr[i][j] != 0){
////每次遇到非0的元素就加1
count++;
}
}
}
确定了有效元素后 便可以创建稀疏数组的列表了
按照列表格式创建列表头:
总共有3列值 分别是 行 列 值
//构建稀疏数组表
int[][] arr2 = new int[count + 1][3];
//定义列表头的值
arr2[0][0] = arr.length;//稀疏数组的行就是原数组每行的数组长度
arr2[0][1] = arr[0].length;//稀疏数组的列就是原数组每列的数组长度
arr2[0][2] = count;//3. 稀疏数组的值就是原数组有效数组的个数
稀疏数组总共有多少行取决于原数组的有效元素有多少个 在这个基础上要加上列表头的那一行 所以就是count + 1
稀疏数组总共有3列 所以数组长度为3
稀疏数组的列表头构建好之后
接下来就是定义每个有效元素所在的坐标
//记录有效元素的数量
int sum = 0;
//需要再次遍历数组来获取有效元素
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
//选出非0的元素转换成稀疏数组
if (arr[i][j] != 0) {
sum++;//sum只要遇到有效元素就+1
//按照行 列 值 的格式定义
arr2[sum][0] = i;//原数组第几行有非0的元素对应的是稀疏数组第几行第一列的数据(行数)
arr2[sum][1] = j;//原数组第几列有非0的元素对应的是稀疏数组第几行第二列的数据(列数)
arr2[sum][2] = arr[i][j];//非零元素的值存放在稀疏的第三列数据(值)
}
}
}
System.out.println();
//遍历稀疏数组
for (int i = 0; i < arr2.length; i++) {
//由于输出的值就为1行 所以只用一个循环
System.out.print(arr2[i][0] + "\t" + arr2[i][1] + "\t" + arr2[i][2] + "\t");
System.out.println();
}
经过以上步骤已经成功压缩数组
当需要读取原数组内的所有元素时,则需要通过稀疏数组进行还原
代码如下:
//还原数组
int[][] arr3 = new int[arr2[0][0]][arr2[0][1]];
//按照原数组的格式 将稀疏数组内的数据遍历出来 因为要跳过稀疏数组的列表头 所以要从第二行开始遍历
for (int i = 1; i < arr2.length; i++) {
//使用含稀疏数组表达式来表示还原数组的下标 简化为 :ars3[1][0] = 22;
arr3[arr2[i][0]][arr2[i][1]] = arr2[i][2];
}
//打印还原后的数组
for (int[] a : arr3) {
for (int b : a) {
System.out.print(b + "\t");
}
System.out.println();
}
五、Arrays类
数组工具类Java.until.Arrays
为数组对象提供一些简单操作的类
Arrays常用方法:
1. 打印数组的每一个元素
Arrays.tostring(int[]);
参数:
int[] 打印的指定数组名
2. 填充数组元素
Arrays.fill(int[], fromIndex, toIndex, val);
参数:
int[] 被分配的指定数组名
fromIndex 分配索引的范围的第一个元素索引 被填充的范围包括此索引
toIndex 分配索引范围的最后一个元素索引 小于此索引
val 分配给数组元素的指定值
3. 对数组元素进行排序 升序形式
Arrays.sort(int[]);
参数:
int[] 进行排序的指定数组名
4. 使用二进制算法搜索数组指定元素的索引
Arrays.binarySearch(int[], key);
注意事项是在数组被排序过才能使用该方法
参数:
int[] 被搜索的指定数组名
key 指定数组的元素
六、数组冒泡排序
原理:
相邻两个元素比较大小。前一个元素比后一个元素大,就交换位置。直到交换到最后一个元素。
每交换一轮,较大的元素会被排在最后面,较小的元素则会排在最前面
每轮排序的元素越来越少,直到没有元素可以交换为止。
冒泡排序代码如下:
//外层循环 表示冒泡轮数
for (int i = 0; i < ars.length - 1; i++) {
//内层循环 前一个元素和后一个元素比较大小
for (int j = 0; j < ars.length - 1; j++) {
//判断如果前一个数比后一个数大
if (ars[j] > ars[j + 1]){
//定义第三方存储
int temp = 0;
temp = ars[j + 1];//先把后一个元素放进temp
ars[j + 1] = ars[j];//再把前一个元素放进后一个元素
ars[j] = temp;//最后把temp里的元素赋值给第一个元素
}
}
内部相邻两个元素交换数值的代码分析如下:
由于每轮排序可能会遇到有相邻两个元素不用交换。
冒泡排序还是会依次走访每一个元素,判断之后才决定是否跳到下一个。
可以写一个优化排序的方法。如果遇到不需要排序的元素时,跳出
在外层循环下定义一个标识位:
在内层循环下写上一个语句:
每次交换数据时 标识位布尔值为true
内层循环结束后建立一个if结构:
内层循环结束后如果没有交换数据 就跳出循环