目录
1.静态初始化:在创建数组时不直接指定数组的元素个数,而是将具体的数据内容进行指定。
一.认识数组
1.为什么使用数组?
像在之前的程序中使用的变量属于基本数据类型,基本数据类型包括整型,字符型,浮点型,这些都是简单的数据类型。对于一些简单的问题,使用这些基本类型就可以了。如果是一些复杂的问题,这些基本数据类型很难解决了!比如,我要计算一个班级中30名同学的平均成绩,从理论上来看是非常简单的,30名同学成绩相加除以30就能得到,如果是在程序中,我们该如何定义了?是写三十个不同的变量?如果有一千个学生的成绩呢?写一千个学生变量?我相信大家不可能定义这么多变量。不知道大家有没有发现,这30个同学是一个班级的,并且需要计算的对象都是float类型的数据,都拥有相同的属性。有没有一种办法,把这些拥有相同属性的数据集中在一起,然后用一个名字(s)来表示这个集中的数据,如果要表示这个集合数据中某一个数据,可以在名字的右下角加一个序号,如:第一个学生成绩s1....,数组的出现就很好的解决了上面这一问题。
2.什么是数组
我们通过上面的例子不难发现,数组就是一组有序数据的集合,存储在数组中的数据用下标进行编号排序,其次数组中的每个元素都属于同一个数据类型,不能把不同的数据类型放在一个数组中,访问数组中不同的数据使用数组中的下标进行访问。
二.一维数组
像上面学生成绩数组s就是一维数组,在使用数组之前,我们必须在程序中定义这个数组,为什么要定义了?我们需要用这种方式来通知计算机,我这个数据的集合是一个数组,我这个数组有哪些数据,有多少个元素,属于那种数据类型。
1.一维数组的定义
1.语法格式
数据类型[] 数组名 = new 数据类型[N];
数据类型:表示数组中存放的元素类型
数据类型[]:表示数组的类型
N:表示数组的长度,也是数组中存储元素的最大数量
int[] array1 = new int[10];
解释:定义了一个整型数组,数组名为array1 ,整型数组中包含10个整型元素
String[] array2 = new String[3];
解释:定义了一个字符串类型数组,数组名为array2,字符串类型数组中可以存储3个字符串元素的数组。
2.注意事项
1.数组名的命名规则和变量名相同,遵循标识符命名规则
2.在定义数组时需要指定数组中的元素个数,就是在方括号[N]表示你要存储的元素个数(其实可以不指定--数组初始化),特别要注意的是数组中第一个元素的下标是从0开始的,数组中最后一个元素的下标是N-1,不存在数组下标N。这跟数学中的逻辑顺序有点不一样,需要引起重视。
3.Java中定义的数组长度可以是变量的这跟C语言中不同(常量表达式)。
2.一维数组的初始化
为什么要初始化数组?初始数组是为了给数组中填充数据元素,就是你要对哪些数据进行处理,你要把这些数据元素添加到数组中才能使用数组进行处理这些元素。要不然数组就不能使用。思考:数组能不能为空或者说数组能不能不初始化?
数组中的初始化可以分为静态初始化和动态初始化
1.静态初始化:在创建数组时不直接指定数组的元素个数,而是将具体的数据内容进行指定。
语法格式1:数据类型[] 数组名称 = new 数据类型[]{具体内容};
语法格式2:数据类型[] 数组名称 = {具体内容};
语法格式3:数据类型[] 数组名称;数组名称 = new 数据类型[]{具体内容};
//数据类型[] 数组名称 = new 数据类型[]{具体内容};
int[] array1 = new int[]{1,2,3,4,5};
//数据类型[] 数组名称 = {具体内容};
int[] arra2 = {1,2,3,4,5};
//数据类型[] 数组名称;数组名称 = new 数据类型[]{具体内容};
int[] array3;
array3 = new int[]{1,2,3,4,5};
2.动态初始化:在创建数组时,直接指定数组中的元素个数。
语法格式1:数据类型[] 数组名 = new 数据类型[存储元素数量];
语法格式2:数据类型[] 数组名;数组名 = new 数据类型[存储元素数量];
//数据类型[] 数组名 = new 数据类型[存储元素数量];
int[] array2 = new int[5];
//数据类型[] 数组名;数组名 = new 数据类型[存储元素数量];
int[] array3;
array3 = new int[5];
3.静动态初始化注意事项
1.静态初始化时, {}中数据类型必须与[]前数据类型一致
2.静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度
3.动态初始化先声明数组变量,再使用"new"关键字来为数组分配内存空间,并初始化数组元素
4.需要指定数组的长度或每个维度的长度,且数组元素的类型与数组的声明类型相同
数组能不能为空?数组能为空,也可以对数组不进行初始化,因为数组中元素会有相对应的默认值。如果数组中为基类类型,默认值就会对应基类类型的默认值,如果是引用类型,默认值就为null。
基类类型默认值图解:
类型 | 默认值 |
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0f |
double | 0.0 |
char | /u0000 |
boolean | false |
4.一维数组对元素的引用
我们对数组定义赋值之后,就能对数组就行引用了,但是要注意的是我们只能一此调用一个数组中的元素,不能对数组整体进行调用。
1.对数组元素中的访问
访问形式 数组名[下标];
比如:a[0],a就是数组的名字,0就是要访问元素的下标.a[0]访问元素在序号为0的元素(数组中逻辑数字是以0开始)。
我们这个地方以整型类型举例
public static void main(String[] args) {
//定义数组并赋值
int[] array = {1,2,3,3,4};
//访问数组元素并赋值给整形类型变量
int ret = array[0];
//打印
System.out.println(ret);
//对数组中的内容进行改变
array[1] = 5;
ret = array[1];
System.out.println(ret);
}
在得到的结果中不难发现,我们可以对以赋值的数组内容进行改变
public static void main(String[] args) {
int[] array = new int[3];
//对数组进行赋值
array[0] = 1;
array[1] = 2;
array[2] = 3;
int ret = array[3];
System.out.println(ret);
ret = array[-1];
System.out.println(ret);
}
像上面这样一段代码,在访问下标的时候超出了数组访问的下标长度,也就是元素超出了元素个数,所以会出现数组越界异常。
注意事项:
1.数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过下标访问其任意位置的元素
2.下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常
2.遍历数组中的元素
数组中的元素只能一个一个的访问,不能整体一次性的进行访问,如果在一个数组中有1000个数据元素,我们该如何访问?我们可以利用在前面所学的循环结构进行访问,这个地方我们要注意的是循环结构还是一个一个的进行访问不是整体进行的,循环结构能帮助我们快速访问数组中元素。
我们还是以整型类型举例
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
System.out.print("使用for循环进行打印:");
for(int i = 0; i < 15;i++){
System.out.printf("%d ",array[i]);
}
System.out.println();
System.out.print("使用while循环进行打印:");
int i = 0;
while(i < 15){
System.out.printf("%d ",array[i]);
i++;
}
System.out.println();
System.out.print("使用for-each循环进行打印:");
for(int x:array){
System.out.printf("%d ",x);
}
}
通过观察代码可以发现,我们使用三种不同的循环结构对数组中每个元素进行相同的打印操作。最后一种打印方式是for循环打印的另一种方式叫for-each,这种方式打印更快捷,更方便。不知道大家有没有发现个问题,在打印数组中的数据的时候,我们打印数据的取值范围是根据数组中最大储存容量决定的,如果数组要增加一个元素,我们是不是就手动改变这个取值范围的值,是不是特别麻烦。有没有方法进行自动获取数组的长度了?有的,在Java中通过数组名.length就能获取到当前数组的长度也就是最大容量。样式代码举例
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
System.out.print("使用for循环进行打印:");
for(int i = 0; i < array.length;i++){
System.out.printf("%d ",array[i]);
}
System.out.println();
System.out.print("使用for-each循环进行打印:");
for(int x:array){
System.out.printf("%d ",x);
}
}
如果不想使用数组长度取值来打印数组中的元素,我们可以使用for-each方式进行打印.
三.二维数组
1.认识二位数组
在开头的介绍数组中我们举例到三十名同班同学一科成绩能用一个一维数组储存起来,如果我们要储存同一个班中三科不同的学生成绩,我们又该如何进行操作?我们可以创建三个不同的数组进行存储三个不同的科目的成绩,那如果是12门不同科目的学生成绩,再创建12个不同的数组?这不就跟创建1000个变量类似了!!!!数组的使用是来解决比较复杂的问题的,所以我们在一维数组的基础上还衍生出了二维数组,三维数组甚至多维数组的概念!像不同科目的成绩问题借助二维数组中就能很好的解决储存问题。
二维数组是一维数组的衍生,该如何理解二维数组呢?其实二维数组中集合的每一个元素都可以看作成一个一维数组,所以二维数组的基本性质同样遵循一维数组中的性质。图解:
2.二维数组语法格式
二维数组中的语法格式跟一维数组中一样,在语法格式上使用静态初始化和动态初始化的格式也一样,这个地方介绍两种出现的比较多的类型。
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
数据类型[][] 数组名称 = {具体内容};
int[][] array = new int[3][4]{具体内容};
int[][] array = {具体内容};
以上格式和一维数组同性质,不在详细介绍仅对int[3][4]展开介绍,此处的3表示二维数组中最多能存储三个一维数组的元素,而4表示的是在一维数组中能存储最多四个同类型同属性的数据元素。同样的在访问二位数组时最大的访问长度是array[3-1][4-1]千万千万要记住哦!!!
3.对二维数组中的元素的访问
1.访问二维数组中的元素
我们以此图为例
public static void main(String[] args) {
int[][] array = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
//访问第一列的第二个元素
System.out.println(array[0][1]);
//访问第二行的第三个元素
System.out.println(array[1][2]);
//访问第三行的第一个元素
System.out.println(array[2][0]);
//如果访问array[3][4] 就会报出数组越界异常
}
2.给二维数组赋值且遍历二维数组
public static void main(String[] args) {
int rows = 5; // 假设你想有5行
int cols = 10; // 假设你想有10列
// 使用变量初始化二维数组
int[][] myArray = new int[rows][cols];
// 打印数组的行数和列数,以验证其大小
System.out.println("行数: " + myArray.length);
System.out.println("列数: " + myArray[0].length);
// 为数组赋值
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
myArray[i][j] = i * j; // 示例赋值
}
}
// 打印数组的内容
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
System.out.print(myArray[i][j] + " ");
}
System.out.println();
}
}
像上面的整型二维数组在以后的学习中我们会遇到很多,Java中不仅仅只有整型类型还有字符类型,字符串类型,浮点型,类类型(在类和对象中详解),都可以使用数组来表示,希望各位老铁在学习之余对这些类型也能试着用一用。
四.数组是引用类型?
1.为什么说数组是引用类型?
在Java中,数组被归类为引用类型,而不是基本类型。这一分类基于数组在内存中的存储方式和其行为的特性。我觉得有以下几种原因。
存储位置
基本类型(如int, double, char等)的值直接存储在变量的内存位置中。而数组,即使是基本类型的数组(如int[]),在Java中也是作为对象处理的。数组对象存储在堆内存中,而变量中存储的是指向数组对象的引用(即内存地址)。
可变大小
数组能够存储多个元素,并且这些元素的数量可以在创建时确定。由于基本类型的大小是固定的,所以它们无法直接支持这种可变大小的特性。数组作为对象,其大小可以在运行时动态确定。
空值
引用类型的变量可以设置为null,表示它不引用任何对象。这对于数组来说也是适用的,你可以有一个int[]类型的变量,它的值为null,表示它不指向任何数组对象。
多态性
作为引用类型,数组也支持Java的多态特性。这意味着一个引用变量可以引用其类型的任何子类或实现的数组对象。
方法参数和返回值
当数组作为方法参数或返回值传递时,传递的是数组的引用,而不是数组内容的副本。这意味着方法内部对数组所做的任何修改都会反映到原数组上。
垃圾回收
由于数组对象存储在堆内存中,它们受Java垃圾回收机制的管理。当没有引用指向一个数组对象时,该对象就变为垃圾回收的目标。
尽管数组是引用类型,但它们的行为在某些方面可能与用户定义的类(如自定义对象)有所不同。例如,数组类型在创建时具有固定的维度和类型,并且数组的长度在创建后是不可变的。但尽管如此,它们仍然被视为对象,并通过引用来操作。
2.引用变量
public static void func() {
int[] array1 = new int[3];
array1[0] = 10;
array1[1] = 20;
array1[2] = 30;
int[] array2 = new int[]{1, 2, 3, 4, 5};
array2[0] = 100;
array2[1] = 200;
array1 = array2;
array1[2] = 300;
array1[3] = 400;
array2[4] = 500;
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]);
}
}
public static void main(String[] args) {
func();
}
理解数组中的变量引用,我们来通过分析上述代码来深入理解:
这段Java代码定义了一个静态方法func,在这个方法中进行了数组的操作。下面我将逐步解释代码的内容以及它的执行结果。
首先,我们创建了两个整型数组array1和array2:
int[] array1 = new int[3];
int[] array2 = new int[]{1,2,3,4,5};
array1是一个长度为3的数组,初始时所有元素的值都是0(Java中数组元素的默认值为基本类型的默认值)。
array2是一个长度为5的数组,初始化时已经赋了值{1,2,3,4,5}。
接下来,我们给array1的三个元素赋值:
array1[0] = 10;
array1[1] = 20;
array1[2] = 30;
此时,array1的内容为{10, 20, 30}。
接着,我们尝试修改array2的前两个元素的值:
array2[0] = 100;
array2[1] = 200;
此时,array2的内容变为{100, 200, 3, 4, 5}。
然后,我们做了一个重要的操作:
array1 = array2;
这行代码并没有合并两个数组或者复制array2的内容到array1。相反,它将array1的引用重新指向了array2的引用,也就是说,现在array1和array2都指向了同一个数组对象。此时,array1原本指向的数组(即{10, 20, 30})由于没有任何引用指向它,因此变得无法访问,并可能在后续的垃圾回收中被清理。
接下来,我们再次修改数组的元素:
array1[2] = 300; // 实际上是修改了array2的第3个元素,因为array1现在指向array2
array1[3] = 400; // 修改了array2的第4个元素
array2[4] = 500; // 修改了array2的第5个元素
经过上述修改后,array2(也是array1因为现在它们指向同一个数组)的内容变为{100, 200, 300, 400, 500}。
最后,我们遍历array2并打印其内容:
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]);
}
总结:这段代码展示了如何通过引用修改数组的内容,以及如何通过重新分配引用使两个变量指向同一个数组对象。同时,它也演示了数组长度在创建后是固定的,尝试访问超出数组长度的索引会抛出ArrayIndexOutOfBoundsException异常(虽然在这段代码中并没有发生这种情况)。
五.数组中的应用场景
数组的出现最主要解决的是储存问题所以在存储数据上数组是比较的使用频繁的,当然他还能作为参数进行传递使用。以下我们通过代码来感受数组的魅力。
1.保存数据
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
for(int x:array){
System.out.println(x);
}
}
2.作为函数参数
1.参数传数组类型
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
modifyArray(array);
// 输出修改后的数组
for (int i : array) {
System.out.print(i + " ");
}
}
public static void modifyArray(int[] arr) {
// 修改数组的第一个元素
arr[0] = 100;
// 添加一个新元素(注意:这通常需要创建一个新的更大的数组)
// 此处为简化示例,我们仅展示修改现有元素,并不真正添加新元素
}
2.数组作为函数返回值
public static void main(String[] args) {
int[] resultArray = getArray();
// 输出返回的数组
for (int i : resultArray) {
System.out.print(i + " ");
}
}
public static int[] getArray() {
// 创建一个新数组并初始化
int[] arr = new int[5];
for (int i = 0; i < arr.length; i++) {
arr[i] = i * 2; // 例如,每个元素是其索引的两倍
}
// 返回数组
return arr;
}
3.修饰数组大小作为返回值
public static void main(String[] args) {
int[] originalArray = {1, 2, 3};
int[] resizedArray = resizeArray(originalArray, 5);
// 输出调整大小后的数组
for (int i : resizedArray) {
System.out.print(i + " ");
}
}
public static int[] resizeArray(int[] original, int newSize) {
// 创建一个新数组,大小为newSize
int[] resized = new int[newSize];
// 将original数组的元素复制到resized数组(如果newSize小于original.length,则只复制部分元素)
for (int i = 0; i < Math.min(original.length, newSize); i++) {
resized[i] = original[i];
}
// 如果newSize大于original.length,则新数组剩余部分可以用默认值填充(这里用0)
for (int i = original.length; i < newSize; i++) {
resized[i] = 0;
}
// 返回新数组
return resized;
}
在这个例子中,resizeArray函数创建了一个新的数组,其大小由newSize参数指定。然后,它将原始数组的元素复制到新数组中(如果新数组比原始数组小,则只复制部分元素),并用默认值填充新数组的剩余部分。最后,它返回这个新数组。
3.数组的练习
1.数组的拷贝
public static void func1(){
// newArr和arr引用的是同一个数组
// 因此newArr修改空间中内容之后,arr也可以看到修改的结果
int[] arr = {1,2,3,4,5,6};
int[] newArr = arr;
newArr[0] = 10;
System.out.println("newArr: " + Arrays.toString(arr));
// 使用Arrays中copyOf方法完成数组的拷贝:
// copyOf方法在进行数组拷贝时,创建了一个新的数组
// arr和newArr引用的不是同一个数组
arr[0] = 1;
newArr = Arrays.copyOf(arr, arr.length);
System.out.println("newArr: " + Arrays.toString(newArr));
// 因为arr修改其引用数组中内容时,对newArr没有任何影响
arr[0] = 10;
System.out.println("arr: " + Arrays.toString(arr));
System.out.println("newArr: " + Arrays.toString(newArr));
// 拷贝某个范围.//[2,4)左闭右开
int[] newArr2 = Arrays.copyOfRange(arr, 2, 4);
System.out.println("newArr2: " + Arrays.toString(newArr2));
}
public static void main(String[] args) {
func1();
}
2.查找数组中指定元素(二分查找)
public static void main(String[] args) {
int[] arr = {2, 3, 4, 10, 40};
int target = 10;
int result = binarySearch(arr, target);
if (result == -1) {
System.out.println("Element not present in array");
} else {
System.out.println("Element is present at index " + result);
}
}
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
// 如果目标元素小于中间元素,则只在左半部分查找
if (arr[mid] > target) {
right = mid - 1;
}
// 如果目标元素大于中间元素,则只在右半部分查找
else if (arr[mid] < target) {
left = mid + 1;
}
// 如果目标元素等于中间元素,返回中间元素的索引
else {
return mid;
}
}
// 如果在数组中找不到目标元素,返回-1
return -1;
}
此章节的学习到此结束,有缺陷的地方希望大家在评论区指出,谢谢 !!!