我的个人博客主页:如果’'真能转义1️⃣说1️⃣的博客主页
关于Java基本语法学习---->可以参考我的这篇博客:《我在VScode学Java》
Java一维数组、二维数组
- 零._.在Java中_什么是数组
- Java 数组是一种数据结构,存储一组相同类型的数据。
- 引用数据类型[^什么是引用数据类型]、默认值、数组长度:
- 默认值
- 存储同种数据类型 -- >考虑隐式转换
- 壹._.一维数组
- 解释:
- 声明与初始化
- 【1】Array Declaration_声明数组:必须先声明数组变量,才能在程序中正常使用数组。
- 【2】Array Initialization_初始化数组(开始分配空间) --->分为 静态初始化和动态初始化和默认初始化三种方式。
- (1)静态初始化 --->是在编译时进行的。
- 获取地址值与哈希码---->地址值是十六进制的,其转换为十进制就是我们的哈希码
- 什么是哈希码:
- 运用
- 为什么不是连续的
- 注意
- 既然整数没有地址值,那为什么还是产生了地址值:
- 存放在哪
- 注意事项
- (2)动态初始化--->运行时进行的。
- 说明
- (3)默认初始化
- 【3】静态初始化与动态初始化的区别
- (1)个数和给定值
- (2)另一个区别是,动态初始化可以在运行时更改数组的大小,而静态初始化则不能。
- 动态分配内存
- 处理数组内容
- 方法:
- 二分查找:
- 排序
- 一些术语:
- 根据学习和书籍上的内容总结一下:
- 贰._.二维数组
- Java二维数组创建
- 多维数组的动态初始化(以二维数组为例)
- Java数组-- >为每一维分配空间
- 从高维开始进行数组初始化的例子:
- Java二维数组实现算法
- 矩阵运算-->乘法
- 九九乘法表:
- 叁._.不规则数组
- 注意事项
- 肆._.其他知识点
- 栈
- 堆
- 通过CursorCode再次理解
- 当两个数组指向同一个小空间时,其中一个数组对小空间中的值发生了改变,那么其他数组再次访问的时候都是修改之后的结果了
零._.在Java中_什么是数组
Java 数组是一种数据结构,存储一组相同类型的数据。
(1)Java和其他高级语言都提供了一种使用一个统一的数组名和不同的下标来唯一确定数组 (array)中的元素的数据结构。
(2)可以用一个标识符封装存储一个元素个数固定且元素类型相同的基本类型数据序列或者对象序列。(是一个简单的线性序列,因此访问速度很快。)
[1] 数组在Java 中是一个对象,可以通过声明一个数组变量来创建。
【在Java中,声明一个数组变量需要指定数组的类型和名称,然后使用方括号指定数组的大小。声明一个包含5个整数的数组 int[] myArray = new int[5];
】
[2] 数组变量包含一个指向数组对象的引用,而数组对象包含实际的数据。
数组变量和数组对象之间的关系:
当你创建了一个数组时,实际上是在内存中分配了一块连续的空间来存储数据,并将数组变量指向该空间的引用存储在变量中。在下文会讲述在Java中数组是怎么存储的方式。
数组是一种对象类型,因此可以使用new关键字来创建一个数组对象 int[] myArray = new int[5];
引用数据类型1、默认值、数组长度:
【1】Java数组是引用数据类型,长度属性为 length。
【2】Java数组下标是从零开始的,Java数组下标访问运算符[ ].
【3】数组元素的默认值:
对于基本数据类型 0,0.0,‘\u000’
对于引用元素数据类型string,object等为 null。
对于Boolean类型,为false
【4】数组的长度:数组名.length。
数组的元素是通过索引访问。索引从 0 开始,索引值从 0 到 array.length-1。
默认值
int[] intArray = new int[5];
System.out.println(intArray[0]); // 输出0
double[] doubleArray = new double[5];
System.out.println(doubleArray[0]); // 输出0.0
char[] charArray = new char[5];
System.out.println(charArray[0]); // 输出空字符
boolean[] boolArray = new boolean[5];
System.out.println(boolArray[0]); // 输出false
String[] strArray = new String[5];
System.out.println(strArray[0]); // 输出null
存储同种数据类型 – >考虑隐式转换
根据不同类型选择不同类型的数组存储同种数据类型的多个值。数组在存储数据的时候,需要结合隐式转换考虑。
需要做到保持数组容器的类型,和存储的数据类型保持一致
例如: int类型的数组容器 ( byte short int )[缺点 范围要把控]
例如: double类型的数组容器double( byte short int long float double )
壹._.一维数组
解释:
Java的一维数组是一种数据结构,用于存储相同类型的元素。【如:存储基本数据类型(如int,float等)和对象类型(如String,Object等)。】
每个元素都可以通过其索引访问,索引从0开始以数组的长度-1结束2。
声明与初始化
Understanding Java Array Declaration and Initialization.
创建数组:分配数据存储的物理空间是在堆上 arrayRefVar = new dataType[arraySize];
【1】Array Declaration_声明数组:必须先声明数组变量,才能在程序中正常使用数组。
声明数组是指创建一个数组变量,但是并没有为其分配内存空间。例如:int[] myArray;
这里我们声明了一个名为myArray的整型数组,但是它并没有被初始化,也就是说它并没有被赋值为一个具体的数组对象。
elementType[] arrayRefVar; // 首选的方法
dataType arrayRefVar[]; // 效果相同,但不是首选方法
double [] douarray;
int inarr[];
在Java语言中,采用 【 数据类型 [ ] 数组名 】的形式,而【数据类型 数组名 [ ] 】是保留了C/C++的形式。如今越来越多的语言的风格都采用了第一种的形式了。
【2】Array Initialization_初始化数组(开始分配空间) —>分为 静态初始化和动态初始化和默认初始化三种方式。
初始化数组是指为数组分配内存空间,并为其赋初值。例如:int[] myArray = new int[]{1, 2, 3};
Java数组的初始化告诉编译器数组的大小和元素的值。这里我们初始化了一个名为myArray的整型数组,它被赋值为一个包含三个元素的数组对象,这三个元素的值分别为1、2、3。
数组可以在声明时初始化,也可以在之后的代码中初始化。
(1)静态初始化 —>是在编译时进行的。
Java数组的静态初始化是指在声明数组时为其分配空间并初始化其元素。这可以通过在声明数组时使用花括号来完成,int[] myArray = new int[]{1, 2, 3};
完整格式: 数据类型 [] 数组名 = new 数据类型[]{元素1,元素2....};
简化格式: 数据类型 [] 数组名 =[ 元素1,元素2,元素3...};
//根据不同类型选择不同类型的数组
范例: intl] array=11,22,33);
范例: doublel array2 ={ 11.1,22.2,33.3);
获取地址值与哈希码---->地址值是十六进制的,其转换为十进制就是我们的哈希码
地址值是对象在内存中的实际物理地址,是在内存中的位置可以通过Java中的System.identityHashCode(Object obj)方法获取。
地址值的主要作用是在底层实现中进行对象的访问和操作。
哈希码是根据对象的内容计算出来的一个int类型的值,根据对象的内容计算出来的一个整数值。
可以通过Java中的Object.hashCode()方法获取。哈希码的主要作用是在集合类中进行对象的查找和比较,例如HashMap、HashSet等。使用了一个常见的哈希码实现,称为"乘法散列”。
需要注意的是,哈希码并不是唯一的,不同的对象可能会有相同的哈希码。
而地址值是唯一的,每个对象在内存中都有一个唯一的地址。
int[] arr = { 1, 2, 3 };
int address = System.identityHashCode(arr);
System.out.println("The address of the array is: " + address);
System.out.println("The address of the array is: " + arr);
第3行输出的是数组在内存中的地址,而第4行输出的是数组中存储的元素。如果想要获取数组在内存中的地址,需要使用System.identityHashCode()方法。
//扩展:
[D@776ec8df
//解释一下地址值的格式含义
//[ : 表示当前是一个数组
//D: 表示当前数组里面的元素都是double类型的
//@: 表示一个间隔符号。 (固定格式),@是没有含义的
//776ec8df: 才是数组真正的地址值, (十六进制)
//平时我们习惯性的会把这个整体叫做数组的地址值
地址值的十六进制转换为十进制就是我们的哈希码
可以使用Java内置的System类中的identityHashCode方法。这个方法返回一个对象的哈希码,可以用来表示对象的地址值。在你的代码中,你可以使用以下代码来获取myArray数组的地址值:System.out.println(System.identityHashCode(myArray));
这个地址值是一个哈希码,不是一个真正的内存地址。因此,它不能用来进行指针算术或其他低级操作。
什么是哈希码:
在Java中,每个对象都有一个默认的哈希码,可以使用hashCode()方法来获取,默认情况下,hashCode()方法返回对象的内存地的整教表示形式,但是,可以通过圈盖hashode()方法来提供自定义哈希码实现。这个自定义哈希码应该是根据对条的内容计算出来的,而不是根据对象的内存地处。这样可以确保具有相同内容的对象具有相同的哈希码,从而提高哈希表的性能。以下是一个简单的Java类,演示如何覆盖hashCode ()方法来提供自定义哈希码实现:
哈希码(Hash Code)是指将任意长度的消息压缩到某一固定长度的消息摘要,通常为一个固定长度的字节序列。哈希码通过一种称为哈希函数的算法来生成,它将任意长度的消息映射到一个固定长度的消息摘要。哈希码的特点是唯一性,即不同的消息会生成不同的哈希码,相同的消息生成的哈希码也是相同的。哈希码常用于数据加密、数据压缩、数据完整性验证等领域。
运用
System.out.println("The address of the array is: " + address);//681842940
System.out.println("The address of the array is: " + System.identityHashCode(arr[0]));//1392838282
System.out.println("The address of the array is: " + System.identityHashCode(arr[1]));// 523429237
System.out.println("The address of the array is: " + System.identityHashCode(arr[2]));//664740647
为什么不是连续的
这是因为Java数组不一定是在内存中连续存储的。Java数组是对象,它们在内存中的存储方式取决于Java虚拟机的实现。在某些情况下,Java虚拟机可能会将数组存储在连续的内存块中,但在其他情况下,它们可能会被分散存储在内存中的不同位置。因此,您不能假设Java数组在内存中是连续存储的。
注意
在Java中,只有对象和数组才会有地址值。基本类型(例如int、boolean等)没有地址值。
Scanner sc = new Scanner(System.in);
int c = sc.nextInt();
System.out.println(Integer.toHexString(System.identityHashCode(c)));
System.out.println(System.identityHashCode(c));
sc.close();
12
2d98a335
764977973
既然整数没有地址值,那为什么还是产生了地址值:
这是因为Java中的所有类型都是对象,包括基本类型。在Java中,基本类型被封装在对象中,这些对象称为包装器类。例如,整数类型int被封装在Integer类中。因此,即使你的变量是一个整数类型,它仍然是一个对象,因此可以使用System.identityHashCode()方法来获取其地址值。
存放在哪
根据Java的内存模型,对象和数组是在堆内存中分配的,而基本类型和引用变量则是在栈内存中分配的。因此,Java中的地址值是指向堆内存中的对象或数组的引用,因此地址值是在堆内存中的。
注意事项
在Java中,堆栈是一种数据结构,用于存储方法调用和本地变量。堆栈的地址值是指指向堆栈顶部的指针。每当方法被调用时,一个新的堆栈帧被创建并推入堆栈中。当方法返回时,堆栈帧被弹出堆栈。因此,堆栈地址值随着方法调用的推入和弹出而变化。
另一方面,堆是用于动态分配内存的区域。在Java中,所有对象都存储在堆中。堆中的对象可以通过引用进行访问,而不是直接访问堆地址值。
因此,堆栈地址值和堆地址值是不同的概念,它们的作用和用途也不同。堆栈地址值用于跟踪方法调用和本地变量,而堆地址值用于跟踪动态分配的对象。
(2)动态初始化—>运行时进行的。
动态初始化:初始化时只指定数组长度,由系统为数组分配初始值。格式:数据类型[]数组名 = new 数据类型数组长度[];
在创建的时候,由我们自己指定数组的长度,由虚拟机给出默认的初始化值
在Java中,动态初始化数组是指在声明数组时,只指定数组的长度,而不指定数组元素的值。例如int[] myArray = new int[5];
在这个代码中,我们声明了动态初始化数组的场景包括:
一个名为myArray的整数数组,并指定其长度为5,由于我们没有指定数组元素的值,因此Java会自动将所有元素初始化为默认值0.
当您需要一个具有固定大小的数组时,但是您不知道数组元素的值。当您需要个具有固定大小的数组时,并且您知道数组元素的值,但是这些值在运行时无法确定
在第二种情况下,您可以使用静态初始化数组来指定数组元素的值。例如:
int[] myArray = {1, 2,3,4,5};
在这个代码中,我们声明了一个名为myArray的整数数组,并指定其元素的值为1、2、3、4和5.
说明
dataType[] arrayName = new dataType[arraySize];
请注意,动态初始化数组时,数组中的元素将自动初始化为其默认值。例如,对于整数数组,所有元素将初始化为0。如果您想要将数组中的元素设置为不同的值,您可以使用循环或逐个分配值。
使用new关键字动态初始化数组时,会在堆中分配一块内存空间,用于存储数组元素。如果您多次使用new关键字初始化数组,则会在堆中分配多个内存空间,每个内存空间都包含各自的数据。
(3)默认初始化
默认初始化数组的值取决于数组的类型。如果数组是基本类型(如int,double等),则默认值为0。如果数组是对象类型,则默认值为null。
【3】静态初始化与动态初始化的区别
(1)个数和给定值
动态初始化是在运行时进行的和元素的值都可以在运行时确定,
而在静态初始化中,数组的大小和元素的值都在编译时确定。
静态初始化:手动指定数组元素,系统会根据元素个数,计算出数组的长度。需求中已经明确了要操作的具体数据,直接静态初始化即可。int[] arr = (11, 22, 33);
动态初始化:手动指定数组长度,由系统给出默认初始化值。只明确元素个数,不明确具体数值,推荐使用动态初始化int[] arr = new int[5];
只要在定义数组时就已经确定了数组的元素个数和元素值,就是静态初始化。
而动态初始化只确定了数组的长度,而数组的元素值需要在后续的代码中进行赋值.
静态初始化时就已经确定了,不需要在new后面的数提类型的[]里面写。
因为在静态初始化时,编译器会根据你提供的元素个数自动计算出数组的长度,因此你只需要在[]里面提供元素即可。
(2)另一个区别是,动态初始化可以在运行时更改数组的大小,而静态初始化则不能。
总的来说,动态初始化和静态初始化都是初始化Java数组的有效方法。它们的主要区别在于初始化时间和数组大小是否可以更改
如果您需要向数组添加后续元素则应该使用动态初始化
如果您使用静态初始化创建Java数组,则无法向数组添加后续元素,因为数组的大小已经在编译时确定
动态添加后续的元素需要什么操作如果您使用动态初始化创建Java数组,则可以使用以下代码向数组添加后续元素:
int[] dynamicArray = new int[5];
dynamicArray[0] = 1;
dynamicArray[1] = 2;
dynamicArray[2] = 3
dynamicArray[3] = 4
dynamicArray[4] = 5;
dynamicArray = Arrays.copyof(dynamicArray,10)
dynamicArray[5] = 6;
dynamicArray[6] = 7;
dynamicArray[7] = 8;
dynamicArray[8] = 9;
dynamicArray[9] = 1;
接
在上面的代码中,我们首先使用动态初始化创建了一个名为dnamicArray 的整数数组,并将其初始化为长度为5的数组,然后,我们使用索引将1、2、3、4和5添加到数组中。
下来,我们使用Arrays.copy()方法将数组的大小更改为10。最后,我们使用索引将6、7、8、9和10添加到数组中。
请注意,如果您使用静态初始化创建Java数组,则无法向数组添加后续元素,因为数组的大小已经在编译时确定。如果您需要向数组添加后续元素,则应该使用动态初始化
使用静态初始化创建Java数组,则无法向数组添加后续元素,因为数组的大小已经在编译时确定的。静态初始化是在创建数组时同时指定数组大小和元素的过程
。
静态初始化创建的数组大小是固定的,因此无法向数组添加后续元素。
(1)如果您需要向数组添加后续元素,则应该使用动态初始化。
(2)您可以根据需要更改size的值,以便在运行时动态调整数组的大小。
数据类型[] 数组名= new 数据类型[]{元素1,元素2....};
,叫做静态初始化。
争态初始化方式。这种方式在定义数组时就已经确定了数组的元素个数和元素值,因此也
数据类型[] 数组名 = new 数据类型[数组长度]
叫做动态初始化数组
其中,数组长度可以是一个整数,也可以是一个变量。这种方式在定义数组时只确定了组的长度,而数组的元素值需要在后续的代码中进行赋值.
动态分配内存
Java还提供了其他的方法,例如ArrayList和LinkedList。这些类允许您在运行时添加和删除元素,而无需手动管理内存分配。
//java.util.Scanner类从用户读取输入。Scanner类允许您从标准输入读取用户输入。
import java.util.ArrayList;
public class MyClass {
public static void main(String[] args) {
ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(1);
intList.add(2);
intList.add(3);
System.out.println(intList); // 输出 [1, 2, 3]
}
}
处理数组内容
一般使用基本的三大循环或者 For-Each 循环。
For-Each 循环或者加强型循环,它能在不使用下标的情况下遍历数组。
int[] intArray = {1, 2, 3, 4, 5};
// 使用for-each循环输出数组元素
for (int i : intArray) {
System.out.println(i);
}
不要在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值,这样会造成代码错误。
越界访问数组 —>警告
警告 越界访问数组是经常会出现的程序设计错误,它会抛出一个运行错误ArrayIndexOut0fBoundsException。
为了避免错误的发生,在使用时应确保所使用的下标不超过arrayRefVar.length-1。
主要原因:是在循环中该使用<的地方误用<=时会犯的错误。
方法:
二分查找:
左闭右闭区间的:
public class BinarySearch {
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) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
左闭右开区间的:
public class BinarySearch {
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid;
}
}
return -1;
}
排序
自带方法:
长度为0的数组并非null
(1)Arrays.sort():该方法使用快速排序算法对数组进行排序。时间复杂度为O(nlogn)。
int[] arr = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
(2)Collections.sort():该方法使用归并排序算法对列表进行排序。时间复杂度为O(nlogn)
List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5));
Collections.sort(list);
System.out.println(list);
一些术语:
anonymous array (匿名数组)
array initializer(数组初始化语法)
binary search (二分查找)
index (下标)
indexed variable(下标变量)
insertion sort (插入排序)
linear search(线性查找)
selection sort (选择排序)
根据学习和书籍上的内容总结一下:
-
声明数组变量并不会给数组分配任何空间。数组变量不是基本数据类型变量。数组变量包含的是对数组的引用。当创建一个数组时,若它的元素是基本数据类型的数值,那么该数组的初始值的默认值和改数据类型是一致的。
-
只有创建数组后才能给数组元素赋值。可以使用new操作符创建数组,语法如下:newelementType[array.Size] (数据类型[数组大小])。
-
数组中的每个元素都是使用语法arravRefVarlindex1 (数组引用变量[下标])表示的。下标必须是一个整数或一个整数表达式。
-
创建数组之后,它的大小就不能改变,使用array.length就可以得到数组的大小。由于数组的下标总是从0开始,所以,最后一个下标总是array.length-1。如果试图引用数组界外的元素,就会发生越界错误。千万不要用下标1访问数组的第一个元素,其实际上对于该元素的下标应该是0。这个错误称为下标过1错误(index off-by-one error)。
-
Java有一个称为数组初始化语法 (array initializer)的表达式,它将数组的声明、创建和初始化合并为一条语句,其语法为:
元素类型[] 数组引用变量={value0,value1,.·,valuek}
-
将数组参数传递给方法时,实际上传递的是数组的引用,更准确地说,被调用的方法可以修改调用者的原始数组的元素。
贰._.二维数组
Java使用大端字节序的优势在于它可以更容易地在不同系统之间进行互操作。这是因为大端字节序是网络协议和文件格式中最广泛使用的字节序。[进行通信和交换数据。]
二维数组是Java中的一种数据结构,它可以被看作是一个由多个一维数组组成的数组。
每个一维数组都代表着一个行,而这些一维数组的长度可以不同。 [用于存储表格数据,例如矩阵、棋盘等等。]
Java二维数组创建
Java中创建二维数组,可以使用以下语法:
//Length1 和 Length2 是整数 【左行右列】
type[][] typeName = new type[Length1][Length2];
//拥有以下3种格式
type[][] arrayName = new type[][]{值 1,值 2,值 3,…,值 n}; // 在定义时初始化
type[][] arrayName = new type[Length1][Length2]; // 给定空间,在赋值
type[][] arrayName = new type[Length1][]; // 数组第二维长度为空,可变化
创建形式
int[][] arrayName = new int[rows][columns];
//创建一个具有3行和4列的二维数组
int[][] arrayName = new int[3][4];
数组名 = new 数据类型[第1维的长度][];
//单独初始化每一行来创建二维数组
int[][] arrayName = new int[3][];
arrayName[0] = new int[4];
arrayName[1] = new int[5];
arrayName[2] = new int[6];
要初始化一个二维数组,可以先创建一个具有所需行数的数组,然后为每一行分配空间,方法是将一个具有所需列数的新数组分配给每一行。
多维数组的动态初始化(以二维数组为例)
直接为每一维分配空间,格式如下:type 可以为基本数据类型和复合数据类型,typeLength1 和 typeLength2 必须为正整数,typeLength1 为行数,typeLength2 为列数。
int[][] a = new int[2][3];
二维数组 a 可以看成一个两行三列的数组。
- 从最高维开始,分别为每一维分配空间,例如:
String[][] s = new String[2][];
s[0] = new String[2];
s[1] = new String[3];
s[0][0] = new String("Good");
s[0][1] = new String("Luck");
s[1][0] = new String("to");
s[1][1] = new String("you");
s[1][2] = new String("!");
type[][] typeName = new type[typeLength1][typeLength2];
Java数组-- >为每一维分配空间
从高维开始进行数组初始化的例子:
a = new int[3][]; // 先为第一维分配空间,注意这时没有为第二维分配空间
a[0] = new int[2]; // 然后为第二维的每一个元素(数组)分配空间
a[1] = new int[1];
a[2] = new int[3];
Java二维数组实现算法
对于矩阵运算,存储矩阵的值,执行加法、减法和乘法等操作。
对于图像处理,表示图像的像素,并对图像应用滤镜或变换。
对于图算法,表示图的邻接矩阵,并执行查找两个节点之间的最短路径等操作
矩阵运算–>乘法
int[][] matrix1 = {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}};
int[][] matrix2 = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}};
int[][] result = new int[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 5; k++) {
result[i][j] += matrix1[i][k] * matrix2[k][j];
}
}
}
// 打印结果
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
System.out.print(result[i][j] + " ");
}
System.out.println();
}
九九乘法表:
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
table[i][j] = (i + 1) * (j + 1);
}
}
for (int i = 0; i < 9; i++) {
for (int j = i; j < 9; j++) {
System.out.print((i+1) + "*" + (j+1) + "=" + table[i][j] + "\t");
}
System.out.println();
}
叁._.不规则数组
在 Java 中,不规则数组也被称为“不规则多维数组”或“交错数组”,它是指嵌套的一维数组长度不一致的数组结构。
声明一个不规则数组的方式和声明一个普通的多维数组类似,只不过需要在声明时指定每个一维数组的长度。例如,声明一个二维不规则数组:
int[][] arr = new int[3][];
arr[0] = new int[]{1, 2, 3};
arr[1] = new int[]{4, 5};
arr[2] = new int[]{6, 7, 8, 9};
上面的代码创建了一个二维不规则数组 arr,其中第一维长度为 3,第二维长度不一致。第一个一维数组包含 3 个元素,第二个一维数组包含 2 个元素,第三个一维数组包含 4 个元素。
我们也可以使用如下方式创建一个不规则数组:
int[][] arr = {{1, 2, 3}, {4, 5}, {6, 7, 8, 9}};
使用循环语句访问不规则数组可以使用维度不同的嵌套循环,例如:
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
上面的代码会输出以下结果:
1 2 3
4 5
6 7 8 9
注意事项
在使用不规则数组时,需要注意以下几点:
- 不规则数组中的每个一维数组的长度可以不同,但是每个一维数组本身必须是一个合法的数组对象,否则会抛出 NullPointerException 异常。
- 创建不规则数组时,必须先创建第一维的数组,然后再依次为每个一维数组分配内存空间。
- 访问不规则数组的元素时,需要使用嵌套循环语句。由于每个一维数组的长度不同,因此需要使用 arr[i].length 来获取第 i 个一维数组的长度。
- 不规则数组的长度不可变,一旦创建后,不能改变数组的大小。如果需要增加或删除元素,需要创建一个新的数组,将旧数组中的元素拷贝到新数组中。
- 不规则数组的性能可能会比规则数组差,因为不规则数组需要使用多个一维数组来实现,而且由于每个一维数组的长度不同,可能会导致内存碎片化的问题。因此,在实际应用中,应该根据具体情况选择合适的数据结构来存储数据。
肆._.其他知识点
栈----方法运行时使用的内存,比如main方法运行,进入方法栈中执行
堆----存储对象或者数组,new来创建的,都存储在堆内存
方法区----存储可以运行的class文件
本地方法栈----JVM在使用操作系统功能的时候使用,和我们开发无关
寄存器----CPU使用,和我们开发无关
栈
方法运行时使用的内存比如main方法运行,进入方法栈中执行。开始执行时会进栈代码执行完毕会出栈
.存取速度比堆要快,栈中的数据大小与生存期必须是确定的,栈数据可以共享。
栈的内存要远远小于堆内存,少用递归
栈:主要用于存储局部变量和对象的引用变量,每个线程都会有一个独立的栈空间,所以线程之间是不共享数据的。
Java确实会在栈中为变量分配内存空间。当变量超出其作用域时,Java会自动释放为该变量分配的内存空间,以便该空间可以立即被另作他用。栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。但是,对于在堆内存中分配的对象,情况有所不同。堆内存中的对象对所有线程可见,因为它们不属于任何特定线程。
堆
new来创建的,都存储在堆内存new 出来的东西会在这块内存中开辟空间并产生地址
堆:主要用于存储实例化的对象,数组。由JVM动态分配内存空间【基本类型的变量和对象的引用变量都在函数的栈内存中分配。】。一个JVM只有一个堆内存,线程是可以共享数据的。
堆内存中的对象对所有线程可见。这意味着堆内存中的对象可以被所有线程访问。这是Java中多线程编程的一个重要方面,因为它允许多个线程同时访问和操作相同的对象。
1.只要是new出来的一定是在堆里面开辟了一个小空间
2.如果new了多次,那么在堆里面有多个小空间,每个小空间中都有各自的数据
通过CursorCode再次理解
当两个数组指向同一个小空间时,其中一个数组对小空间中的值发生了改变,那么其他数组再次访问的时候都是修改之后的结果了
public static void main(String[] args) {
int[] arr1 = (1, 22);
int[] arr2 = arr1;
System.out.println(arr1[0]);
System.out.println(arr2[0]);
arr2[0]=0;
System.out.println(arr1[0]);
System.out.println(arr2[0]);
}
图片来源于网络
引用数据类型是指在Java中,变量存储的是对象的引用而不是对象本身。这意味着变量指向内存中的对象,而不是包含对象本身的值。这与基本数据类型不同,基本数据类型的变量包含它们自己的值。
包括类、接口、数组和枚举类型。当你创建一个对象时,实际上是在堆上分配了一块内存来存储该对象,并返回一个引用,该引用指向该对象的内存地址。你可以将该引用存储在变量中,并使用该变量来访问该对象。// 声明一个类类型的变量 MyClass obj;· ·// 创建一个对象并将其分配给变量 obj = new MyClass();· ·// 访问对象的属性和方法 obj.myMethod();
↩︎这是因为数组的长度是固定的,而数组的索引是从0开始的,所以最后一个元素的索引就是数组长度减1.因此,以数组的长度-1作为数组的结束索引是为了保证不会访问到数组之外的内存空间,从而保证程序的安全性。 ↩︎