目录
数组的说明:
数组是相同类型的数据的集合存储形式,数组存储形式是相同类型的若干个数据,按照添加元素的顺序有一定的先后次序,在数组中,数组中的每一个数据称之为一个元素,每个元素都有一个对应的索引(下标)来访问它们。
数组的特点:
- 数组一旦被创建,它的大小就是不可以改变的,即数组的长度是确定的。
- 数组中的元素的类型必须是相同类型,不允许出现混合类型。(除了 Object[] 数组)
- 数组类型可以是任何数据类型,包括基本类型和引用类型(不包括注解这个引用类型)
- 数组的索引从 0 开始,到数组的长度(数组.length - 1)结束。
- 数组变量属于引用类型,数组也是对象,数组中的每个元素相当于该对象所属的 成员变量 ,数组本身就是对象,Java中对象的存储区域是在 JVM 的 堆中的!所以数组无论是保存原始类型还是其他的引用类型,数组对象本身是在堆中存储的!
数组的初始化方式:
- 静态初始化: 除了用new关键字来创建数组之外,还可以直接在定义数组的同时就为元素分配空间并赋值,此时,数组的长度是确定的!
- 静态初始化方式1: 数据类型[] 数组名 = {元素1,元素2, .......}; or数据类型 数组名[] = {元素1,元素2, .......};
- 静态初始化方式2:数据类型[] 数组名 = new 数据类型[] {元素1,元素2, .......}; or 数据类型数组名[] = new 数组类型[] {元素1,元素2, .......};
- 注意事项:
- new 数组类型[4] {元素1,元素2, .......}; 静态初始化不能指定长度!
- 不能声明后再直接用初始化方式1赋值给变量, int[] array; array={1,2,3}; 声明后再赋值必须用初始化方式2:int[] array; array=new int[]{1,2,3};
- 动态初始化:即数组的定义和为数组元素分配空间并赋值的操作分开进行,数组定义时长度必须指定!
- 动态初始化方式1:数据类型[] 数组名 = new 数据类型[指定长度]; 随后添加数组元素:数组名[索引] = 元素;数组名[索引] = 元素; ……([] 一般都是在数据类型后面,也可以在数组名后面!)
- 动态初始化方式2:数据类型[] 数组名; 数组名=new 数据类型[指定长度];随后添加数组元素:数组名[索引] = 元素;数组名[索引] = 元素; ……([] 一般都是在数据类型后面,也可以在数组名后面!)
- 默认初始化:数组属于引用类型,它的元素相当于类型的实例变量,所以数组一旦分配了空间,其中的每个元素也会按照实例变量同样的方式被隐式的初始化!
- 默认初始化方式1:数据类型[] 数组名 = new 数据类型[指定长度];([] 一般都是在数据类型后面,也可以在数组名后面!)
- 默认初始化方式2:数据类型[] 数组名; 数组名=new 数据类型[指定长度];([] 一般都是在数据类型后面,也可以在数组名后面!)
- 各种数组类型的初始化值:
- 整数:byte[] : 0 、 short[] : 0 、int[] : 0 、long[]: 0 。
- 浮点数:float[] : 0.0 、 double[] : 0.0 。
- 字符:char[] : '\u0000' 。 (不可打印字符,就是一个”空格“!)
- boolean:boolean[] : false 。
- 引用类型: null 。
数组的简单内存分析图示:
比如:十六进制内存地址:
内存图示:
数组的遍历(迭代)方式:
- for循环(正向遍历):
- for循环(逆向遍历):
- for-each循环(增强for循环):
- for-each循环:
- 格式:
- 只是用来遍历数组,集合的一种方式。
- 优点: 代码简洁。
- 缺点:单纯的for-each循环不能涉及和索引相关的操作。
- 格式:
可变参数:
格式: 数据类型... 形参名 or 数据类型 ...形参名
- 可变参数是JDK 1.5 之后加入的新特性。只能用于方法的形参列表中!
- 可变参数的本质是一个数组。
- 方法内部的对可变参数的处理和数组的一样!
- 可变参数和其他数据一起作为形参时,可变参数要放在方法形参列表的最后一位。为了避免歧义!
-
可变参数的作用: 提供了一个方法的参数个数是可变的,解决了部分方法的重载问题(即,数据类型相同但参数个数不同的重载方式!)
-
慎用可变参数!:
-
原因:可变参数接收 0 或者 多个指定类型的参数,可变参数机制通过先创建一个数组,数组的大小是调用者所传入的参数数量,在将参数值传入到数组中,最后再将数组传递给方法使用。
-
在重试性能的场景下,使用可变参数机制要特别的小心,因为可变参数方法的每次调用都会进行一次数组分配和初始化。比较消耗内存开销。
-
如果在调用可变参数的方法是,如果没有传递参数给方法,而如果方法内部又使用了可变参数,编译会通过,但是运行会报异常,最常见的也就是 java.lang.ArrayIndexOutOfBoundException (数组索引越界异常)。(因为 可变参数接收 0 或者 多个指定类型的参数,)
-
Arrays工具类的关于数组的常用API:
- Arrays.toString(数组); 对数组进行遍历查看,并用固定的格式包裹起来。返回一个字符串。
- Arrays.sort(数组); 对数值类型(有值特性)的数组 进行升序排序。
- Arrays.binarySearch(数组); 二分查找,找出指定数组中指定元素对应的索引并返回。 (使用这个方法的前提是数组本身要有序!)
- Arrays.copyOf(要复制的数组,新数组的长度); 从源数组的0索引对应的元素开始复制到指定新数组长度的 源数组对应的索引的元素为止。完成数组的复制。
- Arrays.copyOfRange(要复制的数组,从那个索引位置开始(包括),到什么索引结束(不包括)); 完成数组的区间复制,[开始索引,结束索引),新数组的长度就是区间的长度。
- 复制数组的另一个API:System.arraycopy(源数组,源数组中的元素索引起始位置,目标数组,目标数组中的元素的索引起始位置,要从源数组中复制的元素个数); (使用较多)
- Arrays.equals(数组1,数组2); 比较两个数组中的元素是否全部相等。
- Arrays.fill(要填充的数组,填充的元素); 把数组中的数据全部填充(替换)为指定元素。(很少使用)。重载方法:Arrays.fill(要填充的数组,从什么索引位置开始(包括),到什么索引结束(不包括),要填充的元素); 区间填充。
- Arrays.hashCode(数组); 获取数组的hashCode码。
equals方法 和 == 的区别:== 比较 基本本类比较的是具体的值,比较引用类型比较的是地址值。 equals方法比较的是引用类型的具体存储的值是否相等
数组的两个小案例:
案例1:
// 数组解决,有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,
// 如果兔子都不死,第22个月后的兔子对数是多少?:(斐波拉契数列)
int[] arr = new int[22];
arr[0] = 1;
arr[1] = 1;
for (int i = 2; i < arr.length; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
System.out.println("第: " + (i + 1) + " 个月兔子对数是: " + arr[i] + " 对!");
}
test:
案例2:数组反转1:
//数组反转,方式1:
int[] arr1 = {111, 222, 333, 444, 555, 666, 777, 888, 999};
// 一共交换 数组的长度 / 2 次
int temp = 0;
for (int i = 0; i < arr1.length / 2; i++) {
/*
* 比如第一次交换(i = 0):arr[0] = 111, arr1[arr1.length - 1 -0] = 999 ,
* 第二次交换(i = 1):arr[1] = 222, arr1[arr1.length - 1 -1] = 888 ,
* 第三次交换; …………
**/
temp = arr1[i];
arr1[i] = arr1[arr1.length - 1 - i];
arr1[arr1.length - 1 - i] = temp;
}
System.out.println(Arrays.toString(arr1));
test:
数组反转2:
// 数组反转,方式2:
int[] arr2 = {123, 234, 1345, 456, 567, 678, 789, 8910, 91011};
// 逆向遍历交换
int[] arr3 = new int[arr2.length];
for (int i = arr2.length - 1, tp = 0; i >= 0; i--, tp++) {
arr3[tp] = arr2[i];
}
// 让arr2也指向arr3的内存地址,arr2原来的数据将会被JVM的GC回收。
arr2 = arr3;
System.out.println(Arrays.toString(arr2));
test:
案例三:
模拟数组的动态扩容:
// 模拟数组扩容
long[] arr = new long[0];
Scanner scanner = new Scanner(System.in);
do {
// 每次键录元素都是一个新的数组
long[] newArr = new long[arr.length + 1];
/*这个循环可以用System.arrayCopy方法代替
(当前这个循环第一次不会执行,arr的第一次length为0)*/
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
System.out.print("请输入你要添加的整数元素 : ");
long inputNum = scanner.nextLong();
// 把添加的元素放到最后一位
newArr[newArr.length -1] = inputNum;
// 让初始化的数组变量也指向newArr数组的地址,从而arr这个变量可以操纵newArr这个数组!
// 不会再操纵它上一次操纵过的数组!没有被操纵的数组会被JVM的 GC 回收
arr = newArr;
System.out.println("------------ newArr数组动态扩容并排序后情况: -------------");
// 使用工具类排序 再 输出
Arrays.sort(newArr);
System.out.println(Arrays.toString(newArr));
// 是否继续
System.out.println("是否继续添加元素? y/n or no");
String at = scanner.next();
if ("n".equalsIgnoreCase(at) || "no".equalsIgnoreCase(at)){
break;
}
} while (true);
test:
二维数组:
Java中的二维数组本质上全都是一维数组嵌套得来的,一维数组中的每个元素存储的不是具体的值,而是二维数组里面的一维数组的地址值。二维数组里面的一维数组的长度可以相同,也可以不相同,它们都是独立存在的。!
二维数组内存图示:
比如:([3][3] : 第一个[3]表示二维数组中有多少个一维数组,第二个[3]表示二维数组中的每个一维数组的默认长度是多少,最后的二维数组的长度,如果有创建数组,那么那个一维数组上的索引上的一维数组 就以创建的数组的长度为准!如果没有,就以默认的二维数组的长度为准)
二维数组的十六进制地址:
二维数组里面的一维数组的十六进制地址:
简单内存图示:
二维数组初始化方式:
- 静态初始化: 除了用new关键字来创建数组之外,还可以直接在定义数组的同时就为元素分配空间并赋值,此时,二维数组的长度以及二维数组里面的一维数组的长度是确定的!
- 静态初始化方式1: 数据类型[][] 数组名 = {{元素1,元素2, .......},{元素1,元素2, .......}, ........}; (声明还可以写成:数据类型[] 数组名[] or 数据类型 数组名[][] )
- 静态初始化方式2:数据类型[][] 数组名 = new 数据类型[][] {{元素1,元素2, .......},{元素1,元素2, .......}, ........};(声明还可以写成:数据类型[] 数组名[] or 数据类型 数组名[][] )
- 注意事项:
- new 数组类型[4][]{{元素1,元素2, .......},{元素1,元素2, .......}, ........};静态初始化不能指定长度!
- 不能声明后再直接用初始化方式1赋值给变量, int[][] dimensionalArray; dimensionalArray={{1,2},{34,53},{34,5}}; 声明后再赋值必须用初始化方式2:int[][] dimensionalArray;dimensionalArray=new int[][]{{1,2},{34,53},{34,5}};
- 动态初始化:即数组的定义和为数组元素分配空间并赋值的操作分开进行,二维数组定义时必须指定长度,表示存储多少个一维数组!
- 动态初始化方式1:数据类型[][] 数组名 = new 数据类型[指定存储多少个一维数组][]; 随后添加一维数组:数组名[索引] = new int[]{元素1,元素2, ……}; 修改二维数组里的元素:数据类型[哪一个一维数组][一维数组的索引] = 元素; (声明还可以写成:数据类型[] 数组名[] or 数据类型 数组名[][] )
- 动态初始化方式2:数据类型[][] 数组名; 数组名 = new 数据类型[指定存储多少个一维数组][]; 随后添加一维数组:数组名[索引] = new int[]{元素1,元素2, ……}; 修改二维数组里的元素:数据类型[哪一个一维数组][一维数组的索引] = 元素; (声明还可以写成:数据类型[] 数组名[] or 数据类型 数组名[][] )
- 还可以写成:数据类型[][] 数组名 = new 数据类型[指定存储多少个一维数组][每个一维数组默认的长度是多少]; 可以直接按照二维数组的索引进行赋值!(不过后面也是可以重新创建数组覆盖默认的数组长度的!!二位数的里面的一位数组的长度是可以变的!以最后创建的一维数组为准)
- 默认初始化:数组属于引用类型,它的元素相当于类型的实例变量,所以二维数组一旦分配了空间,其中的每个元素也会按照实例变量同样的方式被隐式的初始化!
- 默认初始化方式1:数据类型[][] 数组名 = new 数据类型[指定存储多少个一维数组][每个一维数组默认的长度是多少];(声明还可以写成:数据类型[] 数组名[] or 数据类型 数组名[][] )
- 默认初始化方式2:数据类型[][] 数组名; 数组名== new 数据类型[指定存储多少个一维数组][每个一维数组默认的长度是多少];(声明还可以写成:数据类型[] 数组名[] or 数据类型 数组名[][] )
- 各种数组类型的初始化值:
- 整数:byte[] : 0 、 short[] : 0 、int[] : 0 、long[]: 0 。
- 浮点数:float[] : 0.0 、 double[] : 0.0 。
- 字符:char[] : '\u0000' 。 (不可打印字符,就是一个”空格“!)
- boolean:boolean[] : false 。
- 引用类型: null 。
二维数组的遍历方式:
- 嵌套for循环(正向遍历):
- 嵌套for循环 (逆向遍历):
- for循环嵌套for-each遍历:
- for-each嵌套for循环遍历:
- for-each嵌套for-each遍历:
Arrays工具类中关于二维数组的三个方法:
-
Arrays.deepToString(二维数组); 对二维数组进行深层次的遍历查看,并用固定的格式包裹起来。返回一个字符串。
-
Arrays.deepEquals(二位数组1,二维数组2); 对两个二维数组中的具体的值做深层次的比较是否相等。
-
Arrays.deepHashCode(二维数组); 深层次获取二维数组的hashCode码。
二维数组的小案例:
案例1:
/*
输出:
* 1
* 1 2
* 1 2 3
* ………………
* */
int[][] dimeArr = new int[20][];
for (int i = 0; i < dimeArr.length; i++) {
// 每个二维数组的长度比上一个多一个
dimeArr[i] = new int[i + 1];
for (int j = 0; j < dimeArr[i].length; j++) {
// 给二维数组添加元素
dimeArr[i][j] = j + 1;
// 输出
System.out.print(dimeArr[i][j] + " ");
}
// 换行
System.out.println();
}
test:
二维数组的经典案例打印杨辉三角:
// 二维数组打印杨辉三角
int[][] yhTriangle = new int[13][];
for (int i = 0; i < yhTriangle.length; i++) {
// 给二维数组里面的每一个一维数组开空间, 外循环循环一次就是创建一个数组
yhTriangle[i] = new int[i + 1];
for (int j = 0; j < yhTriangle[i].length; j++) {
// 二维数组里面的每一个一维数组的第一索引的元素和最后一索引的元素都是1
if (j == 0 || j == yhTriangle[i].length - 1) {
yhTriangle[i][j] = 1;
} else {
/*
思路剖析:
从第三行起,对于不是第一个元素和最后一个元素的元素的值
等于二维数组的上一个一维数组的同一索引的那个数 + 前一个索引的那个数,
就是当前这个一维数组索引的数,依次类推
比如: 二维数组第三个一维数组的元素是 1 2 1 ,
那么二维数组的第四个一维数组的第二个元素(索引为1) 就是
二维数组的第三个一维数组的第二个元素< 2 >(索引为1)
+ 二维数组的第三个一维数组第二个元素的前一个数(索引为1 - 1)< 1 >
(索引为0) 2 + 1 = 3;
那么二维数组的第四个一维数组的第三个元素(索引为2) 就是
二维数组的第三个一维数组的第三个元素< 1 >(索引为2)
+ 二维数组的第三个一维数组的第三个元素(索引为2 - 1)< 2 >
(索引为1) 1 + 2 = 3;
那么二维数组的第四个一维数组 全部元素就应该是 1 3 3 1
*/
yhTriangle[i][j] = yhTriangle[i - 1][j] + yhTriangle[i - 1][j - 1];
}
// 输出当前数组的元素不换行
System.out.print(yhTriangle[i][j] + "\t");
}
// 换行
System.out.println();
}
test:
这篇文章对你有帮助吗?作为一名Java程序工程师,在评论区留下你的困惑或你的见解,大家一起来交流吧!