当前位置: 首页 > news >正文

深入浅出Java数组:从基础到高阶应用

目录

引言

一、数组概述

1.什么是数组?

2.数组的分类?

3.Java数组存储元素的特点?

4.数组优点?

5.数组缺点?

二、一维数组

1.  静态初始化一维数组

2.增强 for 循环(for-each 循环)

3.动态初始化一维数组

4.当一维数组中存储引用时的内存图

5.如何获取数组中的最大值?

6.如果知道值,如何通过值找它的下标?

7.如何将数组中的所有元素反转?

⑴第一种方式:创建一个新的数组。

⑵第二种方式:首尾交换

8.关于main方法的形参args的作用

①在DOS命令窗口中怎么传

②    在IDEA中怎么传?

③应用场景

9.关于方法的可变长度参数?

⑴.语法

⑵.示例代码

⑶.代码解释

⑷.注意事项

10.一维数组的扩容

⑴.System.arraycopy() 方法

⑵. 使用 Arrays.copyOf() 方法

⑶. 手动遍历复制

⑷.关于数组扩容的效率问题

三、二维数组

1. 静态初始化

2. 二维数组动态初始化


引言

数组是Java中最基础且重要的数据结构之一,用于存储相同数据类型的元素集合。本文将全面解析Java数组的特性、用法及常见应用场景。

一、数组概述

1.什么是数组?

①在Java中,数组是一种用于存储多个相同数据类型元素的容器。

②例如一个存储整数的数组:int[] nums = {100, 200, 300};

③例如一个存储字符串的数组:String[] names = {“jack”,“lucy”,“lisi”};

④数组是一种引用数据类型,隐式继承Object。

⑤因此数组也可以调用Object类中的方法。

⑥数组对象存储在堆内存中。

2.数组的分类?

①根据维数进行分类:一维数组,二维数组,三维数组,多维数组。

②根据数组中存储的元素类型分类:基本类型数组,引用类型数组。

③根据数组初始化方式不同分类:静态数组,动态数组。

3.Java数组存储元素的特点?

①数组长度一旦确定不可变。

②数组中元素数据类型一致,每个元素占用空间大小相同。

③数组中每个元素在空间存储上,内存地址是连续的。

④每个元素有索引,首元素索引0,以1递增。

⑤以首元素的内存地址作为数组对象在堆内存中的地址。

⑥所有数组对象都有length属性用来获取数组元素个数。末尾元素下标:length-1

4.数组优点?

①根据下标查询某个元素的效率极高。数组中有100个元素和有100万个元素,查询效率相同。时间复杂度O(1)。也就是说在数组中根据下标查询某个元素时,不管数组的长短,耗费时间是固定不变的。

原因:知道首元素内存地址,元素在空间存储上内存地址又是连续的,每个元素占用空间大小相同,只要知道下标,就可以通过数学表达式计算出来要查找元素的内存地址。直接通过内存地址定位元素。

5.数组缺点?

①随机增删元素的效率较低。因为随机增删元素时,为了保证数组中元素的内存地址连续,就需要涉及到后续元素的位移问题。时间复杂度O(n)。O(n)表示的是线性阶,随着问题规模n的不断增大,时间复杂度不断增大,算法的执行效率越低。(不过需要注意的是:对数组末尾元素的增删效率是不受影响的。)

②无法存储大量数据,(原因:因为很难在内存上找到非常大的一块连续的内存

二、一维数组

1.  静态初始化一维数组

 第一种方式:

 数据类型[ ] 变量名 = new 数据类型[ ] { 元素1,元素2,元素3.... }; 

比如:

int[] arr = new int[]{1,2,3,34,45};

第二种方式:

数据类型[ ] 变量名 = {元素1,元素2,元素3....};

// 方式一:指定数组元素类型,后跟方括号和数组名
数据类型[] 数组名;
// 方式二:指定数组元素类型,数组名后跟方括号
数据类型 数组名[];
// 声明一个整型数组
int[] intArray;
// 声明一个字符串数组
String stringArray[];

通常推荐使用第一种方式,因为它更能清晰地表明这是一个数组类型。

比如:

int[] arr = {1,2,3,3,4,45,6};

注意:在创建数组对象的时候,提前知道数组中应该具体存储哪些元素,建议使用静态初始化

示例代码:

public class ArraysTest {public static void main(String[] args) {// 静态初始化一维数组的第一种方式int[] arr = new int[]{100, 200, 300};String[] names = new String[]{"jack","lucy","tom"};// C/C++的风格(不建议的,知道就行,别人这样写能看懂就行)//int nums[] = new int[]{100, 200, 300};// 静态初始化一维数组的第二种方式:省略newint[] nums = {200, 500, 666, 888};String[] citys = {"北京", "上海", "天津"};//数组也是引用数据类型Animal a1 = new Animal();Animal a2 = new Animal();Animal a3 = new Animal();Animal[] animals = {a1, a2, a3, new Animal()};}
}
class  Animal{}

2.增强 for 循环(for-each 循环)

* 增强for循环/for-each循环。JDK5的新特性。
*
* for each语法结构:
*      for(数据中元素的数据类型 变量名 : 数组名){
*
*      }
*
*      注意:变量名 代表数组中的每个元素。
*
* for each的优点:代码简洁,可读性强。
*
* for each的缺点:没有下标。(如果需求中需要使用到下标,这种方式就差一点。)

示例代码:

/*** 增强for循环/for-each循环。JDK5的新特性。** for each语法结构:*      for(数据中元素的数据类型 变量名 : 数组名){**      }**      注意:变量名 代表数组中的每个元素。** for each的优点:代码简洁,可读性强。** for each的缺点:没有下标。(如果需求中需要使用到下标,这种方式就差一点。)*/
public class ArraysTest1 {public static void main(String[] args) {//静态初始化一维数组int[] arr={100,200,300};String[] names={"jack","lucy","tom"};//遍历arr数组(for-each)for(int num : arr){// num代表数组中的每个元素System.out.println(num);}//遍历names数组(for-each)for(String name : names){System.out.println(name);}}
}

运行结果:

增强 for 循环适用于只需要访问数组元素,而不需要使用索引的场景。

3.动态初始化一维数组

方式:

数据类型[] 数组名 = new 数据类型[数组长度];

示例代码:

import java.util.Random;/*** 一维数组的动态初始化:*      1. 什么时候使用动态初始化一维数组呢?*          当创建数组时,不知道数组中具体存储哪些元素,可以使用动态初始化。*      2. 语法格式:*          数据类型[] 变量名 = new 数据类型[长度];*      3. 动态初始化一维数组之后,数组长度确定,数组中存储的每个元素将采用默认值。*      4. 默认值:*              数据类型            默认值*              ====================================*              byte                0*              short               0*              int                 0*              long                0L*              float               0.0F*              double              0.0*              boolean             false*              char                \u0000*              引用数据类型          null*/
public class ArrayTest04 {public static void main(String[] args) {// 动态初始化一维数组int[] nums = new int[4];for (int num : nums) {System.out.println(num);}String[] names = new String[10];for(String name : names) {System.out.println(name);}System.out.println("=========================");Object[] objs = new Object[5];for (Object obj : objs) {System.out.println(obj);}System.out.println("=========================");// 向objs数组的第三个位置上放一个对象objs[2] = new Object();for (Object obj : objs) {System.out.println(obj);}double[] scores = new double[10];// 创建一个随机数生成器对象Random random = new Random();for (int i = 0; i < scores.length; i++) {scores[i] = random.nextDouble(101);}// 遍历for(double score : scores){System.out.println(score);}}
}

运行结果:

小细节:当一个方法的参数是一个数组的时候,我们怎么传参数?

示例代码:

/*** 当一个方法的参数是一个数组的时候,我们怎么传参数?*/
public class ArrayTest05 {public static void main(String[] args) {// 第一种方式(静态初始化):创建好数组对象,然后传进去int[] nums = {1,2,3,4};display(nums);System.out.println("=======================");// 第二种方式(静态初始化):直接传//display({1,2,3,4}); // 这是错误的。display(new int[]{1,2,3,4}); // 这是正确的。注意这个小细节。System.out.println("=======================");// 动态初始化方式。display(new int[10]);}/*** 遍历一维数组。* @param arr*/public static void display(int[] arr) {for(int num : arr) {System.out.println(num);}}
}

“Array initializer is not allowed here” 错误主要是因为在不恰当的位置使用了数组初始化器,解决的关键在于确保在需要初始化数组时正确使用 new 关键字。

4.当一维数组中存储引用时的内存图

父类Animal:

public class Animal {}

子类Bird:

public class Bird extends Animal{public void fly(){System.out.println("鸟儿在飞翔!");}
}

子类Cat:

public class Cat extends Animal{public void catchMouse(){System.out.println("猫抓老鼠!");}
}

测试类Test:

public class Test {public static void main(String[] args) {//要求:创建一个数组,让该数组既可以存储Cat,又可以存储BirdAnimal[] animals={};//创建一个数组,既能够存储A,又能存储BA a=new A();B b=new B();Cat c=new Cat();Object[] objs={a,b,c};Bird bird=new Bird();//Cat[] cats={c,bird};//编译报错,;类型不统一/不一致Object[] objs2={c,bird};}
}
class A{}
class B{}

当放进去的类型不一致时,编译会报错

测试类Test02:

public class Test1 {public static void main(String[] args) {Bird b = new Bird();Cat c = new Cat();// 创建一个数组,让该数组既可以存储Cat,又可以存储Bird// 数组中存储的不是对象本身,实际上是对象在堆内存当中的地址。存储的是引用。Animal[] animals = {b, c, new Cat(), new Bird()};}
}

当一维数组中存储引用时的内存图

5.如何获取数组中的最大值?

假设首元素是最大的,然后遍历数组中所有元素,只要有更大的,就将其作为最大值。

思考:找出最大值的下标怎么做?

示例代码:

public class ArrayTest4 {public static void main(String[] args) {int[] arr = {1,3,3,4,45,56,6,7,87,8,8898,1,2,1};System.out.println(arr.length);int max = searchMax(arr);System.out.println("最大值是:" + max);int maxIndex = searchMaxIndex(arr);System.out.println("最大值的下标是:" + maxIndex);}/*** 找最大值的下标* @param arr 数组* @return 最大值的下标*/public static int searchMaxIndex(int[] arr) {// 假设第一个元素是最大的。int maxIndex = 0;// 遍历数组for (int i = 0; i < arr.length; i++) {if(arr[i] > arr[maxIndex]) {maxIndex = i;}}return maxIndex;}/*** 从arr数组中找最大值。* @param arr 数组* @return 最大值*/public static int searchMax(int[] arr) {// 假设第一个是最大的int max = arr[0];// 遍历数组//1.法一for (int num : arr) {if(num > max){max = num;}}//2.方案二for (int i = 0; i < arr.length; i++) {if(arr[i] > max) {max = arr[i];}}return max;}
}

6.如果知道值,如何通过值找它的下标?

示例代码:

/*** 知道要查数组中的哪个值,请返回这个值第一次出现处的下标(因为可能重复)*/
public class ArrayTest07 {public static void main(String[] args) {int[] arr = {200,1,33,4,5,110,56,120,6,120,120,7,4,4,43,3,3,3};int elt = 200;int index = findIndexByElt(arr, elt);System.out.println(index >= 0 ? elt + "在数组中的下标是:" + index : "您查找的数据不存在!");}/*** 从arr数组中查找elt元素的下标。(第一次出现处的下标)* @param arr 数组* @param elt 要查找的元素* @return 元素下标*/public static int findIndexByElt(int[] arr, int elt){for (int i = 0; i < arr.length; i++) {if(arr[i] == elt) {return i;}}// 找不到就返回-1return -1;}
}

7.如何将数组中的所有元素反转?

第一种方式:创建一个新的数组。

第二种方式:首位交换。

⑴第一种方式:创建一个新的数组。

public class ArrayTest5 {// 用创建一个新数组的方式进行数组的反转。public static void main(String[] args) {int[] arr = {1,2,3,4,5,6,7,8,9};// 反转int[] newArr = reverse(arr);// 遍历for (int i = 0; i < newArr.length; i++) {System.out.println(newArr[i]);}}public static int[] reverse(int[] arr){// 第一种方式:创建新数组// 动态初始化一维数组int[] newArr = new int[arr.length];// 遍历新数组for (int i = 0; i < newArr.length; i++) {newArr[i] = arr[arr.length - 1 - i];}return newArr;}
}

运行结果:

⑵第二种方式:首尾交换

假设数据有偶数个:
*      {1,2,3,4}
*      第一次循环:{4,2,3,1}
*      第二次循环:{4,3,2,1}
*
*      假设数据有奇数个:
*      {1,2,3,4,5}
*      第一次循环:{5,2,3,4,1}
*      第二次循环:{5,4,3,2,1}
*
*      无论数组中的数据量是奇数还是偶数,循环的次数都是:length / 2

示例代码:

public class ArrayTest6 {// 首尾交换的方式完成数组的反转。public static void main(String[] args) {int[] arr = {1,2,3,4,5,6,7,8,9};// 反转reverse(arr);// 遍历for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}/*** 这种方式完成的数组反转,不但效率高(循环次数少),而且还节省空间,因为不需要new新的数组对象。* @param arr*/public static void reverse(int[] arr){for (int i = 0; i < arr.length / 2; i++) {// 首尾交换// 首 arr[i]// 尾 arr[arr.length - 1 - i]int temp = arr[i];arr[i] = arr[arr.length - 1 - i];arr[arr.length - 1 - i] = temp;}}
}

运行结果:

8.关于main方法的形参args的作用

①接收命令行参数

②在DOS命令窗口中怎么传?

    在IDEA中怎么传?

在DOS命令窗口中怎么传

⑴.在 Java 中,public static void main(String[] args) 是程序的入口方法,其中 String[] args 是该方法的参数,它是一个字符串类型的数组。下面详细介绍它的作用和使用方式:

作用

String[] args 的主要作用是接收从命令行传递给 Java 程序的参数。当你运行一个 Java 程序时,可以在命令行中输入一些额外的信息,这些信息会被存储在 args 数组中,程序可以根据这些参数执行不同的操作,这为程序提供了一定的灵活性和交互性。

关于main方法上的String[] args作用是什么?
`1. 作用:接收命令行参数用的。2. JVM负责调用ArrayTest.main()方法。JVM负责给main方法准备一个String[]一维数组的对象。3. java ArrayTest abc def xyz底层JVM是怎么做的?命令行参数:"abc def xyz"JVM会将以上字符串以“空格”进行拆分,生成一个新的数组对象。最后这个数组对象是:String[] args = {"abc","def","xyz"};4. 命令行参数有什么用?需求:使用该系统的时候,需要提供正确的口令(用户名和密码)如果用户名是admin,并且密码是admin123,则表示合法用户,其他用户是非法的。非法用户直接退出系统。

示例代码:

public class ArrayTest{public static void main(String[] args){System.out.println(args);System.out.println("如果命令在使用的时候,没有提供参数,则数组的长度是"+args.length);}}

运行:

示例代码2:

public class ArrayTest {public static void main(String[] args) {if(args.length != 2){System.out.println("对不起,要使用该系统,至少需要提供用户名和密码,用户名和密码需要通过命令行参数传进来,例如:java ArrayTest 用户名 密码");return;}// 取出用户名String username = args[0];// 取出密码String password = args[1];if(username.equals("admin") && password.equals("admin123")){System.out.println("登录成功,欢迎使用本系统");System.out.println(".....");}else{System.out.println("对不起,您的用户名不存在或者密码错误,请重新进入!");return;}}
}

运行:

// 当两个字符串String进行equals的时候,并且其中有一个字符串是字面量。
// 建议将字面量写到前面。可以避免空指针异常的发生。

示例代码3:

	// 需求:使用该系统的用户包括两种// 一个是普通用户// 一个是超级管理用户// 如果是超级管理员用户,在命令行参数的第一个参数上标注:admin,如果是普通用户第一个参数上标注:user// 假设超级管理员的用户名和密码是:zhangsan 123// 假设普通用户名和密码是:lisi 123if(args.length != 3){System.out.println("对不起,命令行参数不正确,要使用该系统,命令行参数格式必须是:java ArrayTest 角色 用户名 密码");return;}// 取出角色String role = args[0];// 取出用户名String username = args[1];// 取出密码String password = args[2];// 当两个字符串String进行equals的时候,并且其中有一个字符串是字面量。// 建议将字面量写到前面。可以避免空指针异常的发生。if("admin".equals(role) && username.equals("zhangsan") && password.equals("123")){System.out.println("欢迎超级管理员"+username+",请使用本系统!");}else if(role.equals("user") && username.equals("lisi") && password.equals("123")){System.out.println("欢迎操作员"+username+",请使用本系统!");}else{System.out.println("角色不对,或者用户名不存在,可能密码也错了!");}

②    在IDEA中怎么传?

示例代码:

public class ArrayTest7 {public static void main(String[] args) {System.out.println(args[0]);System.out.println(args[1]);}}

运行结果:

解释:

直接访问了 args[0] 和 args[1],也就是数组的第一个和第二个元素。不过,当你运行这个程序时没有传递任何命令行参数,那么 args 数组的长度就为 0。在 Java 中,数组索引是从 0 开始的,一个长度为 0 的数组没有任何元素,所以访问 args[0] 或者 args[1] 就会导致数组越界异常。

如何解决:

1.点右上角Edit Configurations

2.选择要配置的运行项:在左侧的列表中选择 ArrayTest7。

3.设置命令行参数:在右侧的 Program arguments 文本框中输入至少两个参数,参数之间用空格分隔,例如:

重新运行:

③应用场景

  • 配置程序行为:通过命令行参数可以配置程序的不同行为,例如指定文件路径、设置日志级别等。
  • 批量处理任务:可以传递一组数据作为参数,让程序对这些数据进行批量处理。
  • 测试不同输入:在开发和测试过程中,可以通过传递不同的参数来测试程序在各种输入情况下的表现。

总之,String[] args 为 Java 程序提供了一种从外部接收数据的方式,使得程序更加灵活和可配置。

9.关于方法的可变长度参数?

①可变长参数只能出现在形参列表中的最后一个位置。

②可变长参数可以当做数组来处理。

在 Java 中,方法的可变长度参数(Varargs)是一种特殊的语法,它允许你在调用方法时传递不定数量的参数。可变长度参数为开发者提供了更大的灵活性,使得方法可以处理不同数量的输入。以下将详细介绍可变长度参数的相关内容。

⑴.语法

在方法声明中,可变长度参数使用三个连续的点 ... 来表示,其基本语法如下:

public void methodName(Type... parameterName) {// 方法体
}

其中,Type 是参数的类型,parameterName 是可变长度参数的名称。在方法内部,可变长度参数被当作数组来处理。

⑵.示例代码

下面是一个简单的示例,展示了如何使用可变长度参数:

public class VarargsExample {// 定义一个带有可变长度参数的方法public static int sum(int... numbers) {//可变长度参数在方法内部被当作数组处理int total = 0;// 遍历可变长度参数数组for (int num : numbers) {total += num;}return total;}public static void main(String[] args) {// 调用 sum 方法,传递不同数量的参数int result1 = sum(1, 2, 3);int result2 = sum(4, 5);int result3 = sum(6, 7, 8, 9);System.out.println("结果 1为: " + result1);System.out.println("结果 2为: " + result2);System.out.println("结果 3为: " + result3);}
}

运行结果:

⑶.代码解释

  1. 方法定义sum 方法使用了可变长度参数 int... numbers,表示可以接收任意数量的 int 类型参数。
  2. 方法内部处理:在 sum 方法内部,numbers 被当作数组来处理,使用增强 for 循环遍历数组并计算所有元素的总和。
  3. 方法调用:在 main 方法中,分别调用 sum 方法并传递不同数量的参数,程序会根据传递的参数动态计算总和。

⑷.注意事项

  1. 一个方法只能有一个可变长度参数而且可变长度参数必须是方法的最后一个参数否则会导致编译错误。例如,以下代码是不合法的:
// 错误示例:可变长度参数不是最后一个参数
public void invalidMethod(int... numbers, String name) {// 方法体
}

2.可变长度参数可以为空:在调用方法时,可以不传递任何参数给可变长度参数,此时参数数组的长度为 0。例如:

int result = sum(); // 传递空参数

3.可变长度参数与数组的关系:虽然可变长度参数在方法内部被当作数组处理,但在调用方法时,传递数组和传递多个参数是有区别的。例如:

int[] arr = {1, 2, 3};
int result1 = sum(arr); // 传递数组
int result2 = sum(1, 2, 3); // 传递多个参数
sum(new int[]{1,2,3,3,3,4,4,5});
sum(1,2,3,3,3,4,4,5);

这两种调用方式在功能上是等价的,但语法上有所不同。

10.一维数组的扩容

 关于数组的扩容问题:
*      1. 数组的长度一旦确定不可变。
*      2. 如果数组满了,想存储更多的数据,只能创建一个新的数组,将原数组中的数据全部拷贝到新数组当中。这样就可以完成扩容了。
*      3. 怎么优化?建议减少扩容次数。建议预测数据量,创建一个容量差不多的数组。
*      4. 扩容的时候需要使用到数组的拷贝?怎么拷贝?
*          System.arraycopy()

在 Java 中,数组的长度在创建之后确实是不可变的。若要实现数组扩容,通常的做法是创建一个更大的新数组,然后把原数组里的数据复制到新数组中。下面详细介绍几种实现一维数组扩容的方式。

⑴.System.arraycopy() 方法

System.arraycopy() 是 Java 提供的一个高效的数组复制方法,其语法如下:

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
  • src:源数组。
  • srcPos:源数组的起始位置。
  • dest:目标数组。
  • destPos:目标数组的起始位置。
  • length:要复制的元素个数。

示例代码如下:

public class ArrayExpansionExample {public static void main(String[] args) {// 原始数组int[] originalArray = {1, 2, 3, 4, 5};// 扩容后的新数组长度int newLength = originalArray.length * 2;// 创建新数组int[] newArray = new int[newLength];// 使用 System.arraycopy() 方法将原数组元素复制到新数组System.arraycopy(originalArray, 0, newArray, 0, originalArray.length);// 打印新数组元素for (int i = 0; i < newArray.length; i++) {System.out.print(newArray[i] + " ");}}
}

在这个示例中,先定义了一个原始数组 originalArray,接着创建了一个长度为原数组两倍的新数组 newArray,最后使用 System.arraycopy() 方法把原数组的元素复制到新数组中。

底层是C++的

⑵. 使用 Arrays.copyOf() 方法

Arrays.copyOf() 是 Java 标准库中 Arrays 类提供的一个便捷方法,它可以创建一个新数组并将原数组的元素复制到新数组中,同时可以指定新数组的长度。示例代码如下:

import java.util.Arrays;public class ArrayExpansionExample2 {public static void main(String[] args) {// 原始数组int[] originalArray = {1, 2, 3, 4, 5};// 扩容后的新数组长度int newLength = originalArray.length * 2;// 使用 Arrays.copyOf() 方法进行数组扩容int[] newArray = Arrays.copyOf(originalArray, newLength);// 打印新数组元素for (int i = 0; i < newArray.length; i++) {System.out.print(newArray[i] + " ");}}
}

Arrays.copyOf() 方法内部实际上也是调用了 System.arraycopy() 方法,它简化了数组扩容的代码编写。

⑶. 手动遍历复制

除了使用上述方法,还可以通过手动遍历原数组,将元素逐个复制到新数组中。示例代码如下:

public class ArrayExpansionExample3 {public static void main(String[] args) {// 原始数组int[] originalArray = {1, 2, 3, 4, 5};// 扩容后的新数组长度int newLength = originalArray.length * 2;// 创建新数组int[] newArray = new int[newLength];// 手动遍历复制元素for (int i = 0; i < originalArray.length; i++) {newArray[i] = originalArray[i];}// 打印新数组元素for (int i = 0; i < newArray.length; i++) {System.out.print(newArray[i] + " ");}}
}

这种方法的效率相对较低,尤其是在处理大规模数组时,因为它需要手动进行元素复制操作,而 System.arraycopy() 和 Arrays.copyOf() 方法是经过优化的本地方法,执行效率更高。

⑷.关于数组扩容的效率问题

数组扩容会影响程序的执行效率,因为每次扩容都需要创建新数组并复制原数组的元素。所以在实际开发中,尽可能提前预测数据量,创建一个接近所需数量的数组,以此减少扩容的次数,从而提高程序的性能。

三、二维数组

1. 静态初始化

  • 完整语法

    int[][] arr = new int[][]{{1, 2}, {3}, {4, 5}};
  • 简化语法(仅限声明时使用):

int[][] arr = {{1, 2}, {3}, {4, 5}};

示例代码:

/*** 1.二维数组的静态初始化* 2.二维数组中元素的访问。*/
public class ArrayTest8 {public static void main(String[] args) {// 静态初始化二维数组的第一种方式// 定义一个二维数组 arr,它包含多个一维数组// 这里每个大括号 {} 代表一个一维数组,其中包含了具体的元素int[][] arr = new int[][]{{1,2,3},{4,5,6,7,8},{1,1,1,1,1,1,1,1,1,1,100}};// 输出该二维数组中包含的一维数组的数量// arr.length 表示二维数组中一维数组的个数System.out.println("该二维数组当中有" + arr.length + "个一维数组!");// 输出每个一维数组的长度// arr[0].length 表示二维数组中第一个一维数组的元素个数System.out.println(arr[0].length); // 3// arr[1].length 表示二维数组中第二个一维数组的元素个数System.out.println(arr[1].length); // 5// arr[2].length 表示二维数组中第三个一维数组的元素个数System.out.println(arr[2].length); // 11// 找二维数组中第一个一维数组中的第一个元素// 下面两行代码是分步获取元素的过程// 先获取二维数组中的第一个一维数组/*int[] arr0 = arr[0];// 再从第一个一维数组中获取第一个元素int arr00 = arr0[0];*/// 合并以上两步,直接通过双重索引访问二维数组中的元素// arr[0][0] 表示二维数组中第一个一维数组的第一个元素int arr00 = arr[0][0];// 输出二维数组中第一个一维数组的第一个元素System.out.println("二维数组中第一个一维数组中的第一个元素:" + arr00);// 访问最后一个一维数组的最后一个元素// arr.length - 1 表示二维数组中最后一个一维数组的索引// arr[arr.length - 1].length - 1 表示最后一个一维数组中最后一个元素的索引System.out.println("最后一个一维数组的最后一个元素:" + arr[arr.length - 1][arr[arr.length - 1].length - 1]);// 修改最后一个一维数组的最后一个元素的值为 456arr[arr.length - 1][arr[arr.length - 1].length - 1] = 456;// 再次输出修改后最后一个一维数组的最后一个元素的值System.out.println("最后一个一维数组的最后一个元素:" + arr[arr.length - 1][arr[arr.length - 1].length - 1]);// 静态初始化二维数组的第二种方式int[][] arr2 = {{1,2,3},{4,5,6,7,8},{1,1,1,1,1,1,1,1,1,1,100}};// 访问第二个一维数组的第二个元素System.out.println(arr2[1][1]);}
}

2. 二维数组动态初始化

⑴等长二维数组(每个内层数组长度相同):

int[][] arr = new int[3][4];

外层数组长度3,每个内层数组长度4,初始值为0。

⑵不等长二维数组(内层数组长度可变):

int[][] arr = new int[3][];  // 外层长度3,内层未初始化(均为null)
arr[0] = new int[2];         // 手动初始化各内层数组
arr[1] = new int[3];
arr[2] = new int[4];

相关文章:

深入浅出Java数组:从基础到高阶应用

目录 引言 一、数组概述 1.什么是数组&#xff1f; 2.数组的分类&#xff1f; 3.Java数组存储元素的特点&#xff1f; 4.数组优点&#xff1f; 5.数组缺点&#xff1f; 二、一维数组 1. 静态初始化一维数组 2.增强 for 循环&#xff08;for-each 循环&#xff09; 3…...

基于 Nginx 的 CDN 基础实现

概览 本文是对基于Nginx的CDN网络的学习笔记&#xff0c;阅读的代码为&#xff1a;https://github.com/leandromoreira/cdn-up-and-running 其中&#xff0c;先确定CDN中的一些基础概念&#xff1a; Balancer&#xff1a;负载均衡&#xff0c;即请求数据的流量最开始打到Bal…...

讲人话的理解ai学习原理

通过把各种东西打上分数标签存起来。ai不花算力是不可能的&#xff0c;需要巨大的算力&#xff0c;需要要大量gpu芯片&#xff0c;如果大大降低成本&#xff0c;就需要蒸馏别人成果&#xff0c;把这些参数偷偷弄过来。 比如”猫睡在石头上感觉很凉快&#xff0c;很舒服&#x…...

Spring boot整合quartz方法

目录 1.定时任务 1.quartz说明 2.Quartz提供了不同的数据存储策略以管理作业调度信息&#xff1a; 1.Quartz引入依赖 2.开发定时任务 &#xff08;1&#xff09;更新定时任务 &#xff08;2&#xff09;停止定时任务 &#xff08;3&#xff09;唤醒定时任务 &#xff…...

网站改HTTPS方法

默认的网站建设好后打开的样子那看起来像是钓鱼网站&#xff0c;现在的浏览器特别只能&#xff0c;就是你新买来的电脑默认的浏览器同样也会出现这样“不安全”提示。 传输协议启动了向全球用户安全传输网页内容的流程。然而&#xff0c;随着HTTPS的推出&#xff0c;传输协议通…...

数据中台是什么?:架构演进、业务整合、方向演进

文章目录 1. 引言2. 数据中台的概念与沿革2.1 概念定义2.2 历史沿革 3. 数据中台的架构组成与关键技术要素解析3.1 架构组成3.2 关键技术要素 4. 数据中台与其他平台的对比详细解析 5. 综合案例&#xff1a;金融行业数据中台落地实践5.1 背景5.2 解决方案5.3 成果与价值 6. 方向…...

Java Stream API:高效数据处理的利器引言

Java Stream API&#xff1a;高效数据处理的利器引言 在 Java 编程中&#xff0c;数据处理是一项极为常见且关键的任务。传统的 for 循环在处理数据集合时&#xff0c;往往会导致代码变得冗长、复杂&#xff0c;这不仅增加了代码的编写难度&#xff0c;还降低了代码的可读性和…...

qml之Text 组件显示当前时间

在 QML 中,显示时间的常用组件是 Text,结合 JavaScript 时间函数或者 Qt 的时间模块来实现动态时间显示。虽然 QML 没有专门用于显示时间的组件,但可以通过 Text 来显示格式化后的时间信息。 1. 使用 Text 组件显示当前时间 示例代码: import QtQuick 2.15 import QtQui…...

两栏布局、三栏布局、水平垂直居中

文章目录 1 两栏布局1.1 浮动 margin1.2 浮动 BFC1.3 flex布局1.4 左绝父相 margin1.5 右绝父相 方向定位 2 三栏布局2.1 子绝父相 margin2.2 flex布局2.3 浮动 margin2.4 圣杯布局2.5 双飞翼布局 3 水平垂直居中3.1 绝对定位 translate3.2 绝对定位 margin3.3 绝对定位…...

Hanoi ( 2022 ICPC Southeastern Europe Regional Contest )

Hanoi &#xff08; 2022 ICPC Southeastern Europe Regional Contest &#xff09; The original problem “Towers of Hanoi” is about moving n n n circular disks of distinct sizes between 3 3 3 rods. In one move, the player can move only the top disk from on…...

Matplotlib基础01( 基本绘图函数/多图布局/图形嵌套/绘图属性)

Matplotlib基础 Matplotlib是一个用于绘制静态、动态和交互式图表的Python库&#xff0c;广泛应用于数据可视化领域。它是Python中最常用的绘图库之一&#xff0c;提供了多种功能&#xff0c;可以生成高质量的图表。 Matplotlib是数据分析、机器学习等领域数据可视化的重要工…...

SMU寒假训练第二周周报

训练情况 本周是第二周&#xff0c;训练情况比第一周好一点点&#xff0c;也仅仅是好一点点&#xff0c;经过春节以及后遗症&#xff0c;牛客更是打的稀烂&#xff0c;还不如去年&#xff0c;都不知道自己在干嘛&#xff0c;训练赛情况也非常糟糕&#xff0c;还要去搞社会实践…...

解锁全新视界:一键畅享 360 度全景图与多格式转换

软件介绍 各位朋友&#xff0c;大家好&#xff01;今天要给大家引荐一款超实用的全景图转换“神器”——Pano2VR Pro 的最新版本。在当今这个追求极致视觉体验的时代&#xff0c;它宛如一把神奇的钥匙&#xff0c;能够解锁全新的视觉领域&#xff0c;将平平无奇的不同角度图像…...

python:面向对象案例烤鸡翅

自助烤鸡翅的需求&#xff1a; 1.烤鸡翅的时间和对应的状态&#xff1a; 0-4min :生的 4-7min:半生不熟 7-12min&#xff1a;熟了 12min以上&#xff1a;烤糊了 2.添加调料&#xff1a; 客户根据自己的需求添加 定义烤鸡翅的类、属性和方法&#xff0c;显示对象的信息 …...

游戏外挂原理解析:逆向分析与DLL注入实战(植物大战僵尸

目录 1.前言2.外挂类型3.前置知识4.CE查找基质4.1 逐步分析4.2 暴力搜索5.实现数值外挂6.dll导入表注入7.实现行为外挂(无敌类型)8.源码下载与外挂进阶本篇原文为:游戏外挂原理解析:逆向分析与DLL注入实战(植物大战僵尸)。 更多C++进阶、rust、python、逆向等等教程,可…...

【10.10】队列-设计自助结算系统

一、题目 请设计一个自助结账系统&#xff0c;该系统需要通过一个队列来模拟顾客通过购物车的结算过程&#xff0c;需要实现的功能有&#xff1a; get_max()&#xff1a;获取结算商品中的最高价格&#xff0c;如果队列为空&#xff0c;则返回 -1add(value)&#xff1a;将价格为…...

android的ViewModel和LiveData 简介

ViewModel ViewModel 的优势 ViewModel 的替代方案是保存要在界面中显示的数据的普通类。在 activity 或 Navigation 目的地之间导航时&#xff0c;这可能会造成问题。此时&#xff0c;如果您不利用保存实例状态机制存储相应数据&#xff0c;系统便会销毁相应数据。ViewModel…...

Linux系统之free命令的基本使用

Linux系统之free命令的基本使用 一、free命令介绍二、free命令的使用帮助2.1 free命令的帮助信息2.2 free命令帮助解释 三、free命令的基本使用3.1 显示内存使用情况3.2 新增总计条目3.3 显示内存详细信息 四、注意事项 一、free命令介绍 free 命令是 Linux 系统中用于显示系统…...

大模型赋能网络安全整体应用流程概述

一、四个阶段概述 安全大模型的应用大致可以分为四个阶段: 阶段一主要基于开源基础模型训练安全垂直领域的模型; 阶段二主要基于阶段一训练出来的安全大模型开展推理优化、蒸馏等工序,从而打造出不同安全场景的专家模型,比如数据安全领域、安全运营领域、调用邮件识别领…...

SpringCloud - Nacos注册/配置中心

前言 该博客为Nacos学习笔记&#xff0c;主要目的是为了帮助后期快速复习使用 学习视频&#xff1a;7小快速通关SpringCloud 辅助文档&#xff1a;SpringCloud快速通关 一、简介 Nacos官网&#xff1a;https://nacos.io/docs/next/quickstart/quick-start/ Nacos /nɑ:kəʊ…...

面试准备——Java理论高级【笔试,面试的核心重点】

集合框架 Java集合框架是面试中的重中之重&#xff0c;尤其是对List、Set、Map的实现类及其底层原理的考察。 1. List ArrayList&#xff1a; 底层是动态数组&#xff0c;支持随机访问&#xff08;通过索引&#xff09;&#xff0c;时间复杂度为O(1)。插入和删除元素时&#…...

AI伴读-清华大学104页《DeepSeek:从入门到精通》

辅助工具&#xff1a;deepseek、豆包AI伴读 官网&#xff1a;DeepSeekDeepSeek, unravel the mystery of AGI with curiosity. Answer the essential question with long-termism.https://www.deepseek.com/https://www.deepseek.com/清华大学104页《DeepSeek&#xff1a;从入…...

unity学习34:角色相关3,触发器trigger,铰链 hingejoint 等 spring joint, fixed joint

目录 1 触发的实现条件 1.1 碰撞的的实现条件 1.2 触发的实现条件 1.3 触发器trigger&#xff0c;直接拿 碰撞器collider修改下配置即可 2 触发器相关实验&#xff1a;触发开门效果 2.0 目标 2.1 player物体的属性 2.2 新建一个trigger 物体 2.3 新建一个被trigger 控…...

HarmonyOS Next 方舟字节码文件格式介绍

在开发中&#xff0c;可读的编程语言要编译成二进制的字节码格式才能被机器识别。在HarmonyOS Next开发中&#xff0c;arkts会编译成方舟字节码。方舟字节码长什么样呢&#xff1f;我们以一个demo编译出的abc文件&#xff1a; 二进制就是长这样&#xff0c;怎么去理解呢&…...

计算机视觉语义分割——Attention U-Net(Learning Where to Look for the Pancreas)

计算机视觉语义分割——Attention U-Net(Learning Where to Look for the Pancreas) 文章目录 计算机视觉语义分割——Attention U-Net(Learning Where to Look for the Pancreas)摘要Abstract一、Attention U-Net1. 基本思想2. Attention Gate模块3. 软注意力与硬注意力4. 实验…...

html 列动态布局

样式说明&#xff1a; /* 列动态布局&#xff0c;列之间以空格填充 */ li {display: flex;/* flex-direction: column; */justify-content: space-between; }...

DeepSeek开源多模态大模型Janus-Pro部署

DeepSeek多模态大模型部署 请自行根据电脑配置选择合适环境配置安装conda以及gitJanus 项目以及依赖安装运行cpu运行gpu运行 进入ui界面 请自行根据电脑配置选择合适 本人家用电脑为1060&#xff0c;因此部署的7B模型。配置高的可以考虑更大参数的模型。 环境配置 安装conda…...

DeepSeek结合Langchain的基本用法

DeepSeek结合Langchain的基本用法 DeepSeek 基于Openai接口规范的Prompt应答Deepseek结合LangchainDeepSeek 基于langchain的结构化返回 DeepSeek 基于Openai接口规范的Prompt应答 首先我们需要先基于pip 安装 pip install openai最开始我们先熟悉如何使用openai的接口规范&a…...

Redis持久化的两种方式:RDB和AOF

redis中的数据存储在缓存中&#xff0c;如果没有持久化的策略&#xff0c;Redis一旦宕机&#xff0c;那么将会导致数据丢失&#xff1b;因此redis提供了以下两种持久化方式&#xff1a;RDB和AOF 一般来说&#xff0c;大部分公司对这两种方式都是同时开启的 一、RDB RDB策略全…...

每日一题——131.分割回文串

题目链接&#xff1a;131. 分割回文串 - 力扣&#xff08;LeetCode&#xff09; 代码&#xff1a; class Solution { private:vector<vector<string>> result;vector<string> path;void backtracking (const string& s,int startindex){if(startindex …...