Java 数组在内存中的结构是怎样的?数组访问、遍历、复制、扩容、缩容如何编写代码?

news2024/11/15 15:40:15

Java是一门面向对象的编程语言,数组是其中的重要数据结构之一。在Java中,数组是一种固定长度、有序的数据结构,可以存储一组相同数据类型的元素。在本文中,我们将详细介绍Java数组在内存中的结构。

Java数组的定义

在Java中,数组是一种对象,可以用关键字new创建。Java数组可以是一维的,也可以是多维的,如二维数组、三维数组等。

Java数组的定义格式如下:

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

其中,数据类型表示数组中存储的元素的类型,数组名是数组的标识符,数组长度表示数组中元素的个数。

例如,定义一个长度为5的整型数组,可以使用以下代码:

int[] arr = new int[5];

Java数组的内存结构

Java数组在内存中的结构是连续的存储空间。数组中的每个元素在内存中占据相同的空间,并且存储顺序是从数组的第一个元素开始依次存储。

下图是一个长度为5的整型数组在内存中的结构示意图:

|-----------------------------------------------------|
|                 |                 |                 |
|  arr[0] 的值     |  arr[1] 的值     |  arr[2] 的值     |  arr[3] 的值     |  arr[4] 的值     |
|                 |                 |                 |
|-----------------------------------------------------|

上图中,每个数组元素占据4个字节的空间,因为整型数据类型在Java中占据4个字节的空间。在内存中,数组的首地址指向第一个元素的地址,数组的最后一个元素存储在数组末尾的位置。

Java数组的访问

在Java中,数组的元素可以通过数组下标访问。数组下标从0开始,依次递增,直到数组的长度减1。下面是访问数组元素的示例代码:

int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;

System.out.println(arr[0]); // 输出1
System.out.println(arr[1]); // 输出2
System.out.println(arr[2]); // 输出3
System.out.println(arr[3]); // 输出4
System.out.println(arr[4]); // 输出5

在上面的示例代码中,我们首先创建了一个长度为5的整型数组,然后分别给数组的前5个元素赋值。最后,通过数组下标访问数组的元素,并将元素的值打印出来。

需要注意的是,如果访问数组中不存在的元素,将会抛出ArrayIndexOutOfBoundsException异常。

多维数组的内存结构

在Java中除了一维数组,Java还支持多维数组。多维数组可以看作是一维数组的扩展,它可以是二维、三维,甚至可以是更高维度的数组。

对于二维数组,可以将其看作是一组一维数组的集合,每个一维数组中存储着相同的元素类型。在内存中,二维数组按行存储,即每行的元素是连续存储的。

下面是一个二维数组在内存中的结构示意图:

|-----------------------------------------------------|
|                 |                 |                 |
|  arr[0][0] 的值  |  arr[0][1] 的值  |  arr[0][2] 的值  |  
|                 |                 |                 |
|-----------------------------------------------------|
|                 |                 |                 |
|  arr[1][0] 的值  |  arr[1][1] 的值  |  arr[1][2] 的值  |
|                 |                 |                 |
|-----------------------------------------------------|
|                 |                 |                 |
|  arr[2][0] 的值  |  arr[2][1] 的值  |  arr[2][2] 的值  |
|                 |                 |                 |
|-----------------------------------------------------|

上图中,arr是一个3行3列的整型数组,每个元素占据4个字节的空间。在内存中,二维数组的每个元素都可以通过两个下标访问。例如,访问数组中第2行第3列的元素可以使用以下代码:

int[][] arr = new int[3][3];
arr[1][2] = 3;
System.out.println(arr[1][2]); // 输出3

在Java中,多维数组的定义和访问方式与一维数组类似。例如,定义一个3行4列的二维数组可以使用以下代码:

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

在定义多维数组时,可以只指定其中一维的长度,例如以下代码定义了一个长度为3的一维数组和一个长度为5的二维数组:

int[] arr1 = new int[3];
int[][] arr2 = new int[5][];

需要注意的是,在定义二维数组时,每个一维数组的长度可以不同。例如,以下代码定义了一个长度为3、4、5的三个一维数组组成的二维数组:

int[][] arr = new int[3][];
arr[0] = new int[3];
arr[1] = new int[4];
arr[2] = new int[5];

Java数组的拷贝

Java数组的拷贝操作可以将一个数组的元素复制到另一个数组中。Java中提供了两种数组拷贝方式:浅拷贝和深拷贝。

浅拷贝是指将一个数组的引用赋给另一个数组,这样两个数组引用同一块内存空间。当修改其中一个数组的元素时,另一个数组的对应元素也会被修改。以下是一个浅拷贝的示例:

int[] arr1 = {1, 2, 3};
int[] arr2 = arr1;
arr2[0] = 4;
System.out.println(Arrays.toString(arr1)); // 输出 [4, 2, 3]
System.out.println(Arrays.toString(arr2)); // 输出 [4, 2, 3]

在上面的示例中,我们首先定义了一个包含元素1、2、3的一维数组arr1,然后将其引用赋给arr2。接着,我们修改了arr2的第一个元素为4,最后输出了arr1arr2的元素值,发现两个数组的第一个元素都变成了4,这说明浅拷贝操作修改了两个数组的元素。

深拷贝是指将一个数组的所有元素逐个复制到另一个数组中,这样两个数组在内存中占据不同的空间。当修改其中一个数组的元素时,另一个数组的对应元素不会受到影响。以下是一个深拷贝的示例:

int[] arr1 = {1, 2, 3};
int[] arr2 = Arrays.copyOf(arr1, arr1.length);
arr2[0] = 4;
System.out.println(Arrays.toString(arr1)); // 输出 [1, 2, 3]
System.out.println(Arrays.toString(arr2)); // 输出 [4, 2, 3]

在上面的示例中,我们首先定义了一个包含元素1、2、3的一维数组arr1,然后使用Arrays.copyOf()方法将其复制到arr2中。接着,我们修改了arr2的第一个元素为4,最后输出了arr1arr2的元素值,发现只有arr2的第一个元素变成了4,arr1没有受到影响,这说明深拷贝操作没有修改原始数组。

需要注意的是,对于多维数组,数组拷贝操作只会复制数组的第一维。如果需要对多维数组进行深拷贝,可以使用循环或递归方式逐个复制所有元素。

Java数组的排序

Java中提供了多种数组排序算法,常用的有冒泡排序、插入排序、选择排序、快速排序、归并排序等。以下是Java中常用的几种排序算法:

冒泡排序

冒泡排序是一种简单的排序算法,它重复地遍历数组,每次比较相邻的两个元素,如果顺序错误则交换它们的位置,直到遍历完整个数组。

以下是一个冒泡排序的示例:

int[] arr = {5, 2, 8, 1, 4};
for (int i = 0; i < arr.length - 1; i++)
```java
    for (int j = 0; j < arr.length - i - 1; j++) {
        if (arr[j] > arr[j + 1]) {
            int temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}
System.out.println(Arrays.toString(arr)); // 输出 [1, 2, 4, 5, 8]

在上面的示例中,我们首先定义了一个包含元素5、2、8、1、4的一维数组arr,然后使用两个嵌套的for循环遍历整个数组。内部循环每次比较相邻的两个元素,如果前面的元素比后面的元素大,则交换它们的位置。外部循环控制了循环次数,每一轮循环都会把最大的元素交换到数组的最后一个位置。最后输出排序后的数组,得到[1, 2, 4, 5, 8]。

冒泡排序的时间复杂度为O(n^2),虽然它的实现简单,但在处理大量数据时效率较低。

快速排序

快速排序是一种高效的排序算法,它基于分治的思想,通过选定一个基准元素,将数组分成两个部分,其中一个部分的所有元素都比基准元素小,另一个部分的所有元素都比基准元素大。然后递归地对两个部分进行排序。

以下是一个快速排序的示例:

public static void quickSort(int[] arr, int left, int right) {
    if (left < right) {
        int pivotIndex = partition(arr, left, right);
        quickSort(arr, left, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, right);
    }
}

private static int partition(int[] arr, int left, int right) {
    int pivot = arr[left];
    int i = left + 1;
    int j = right;
    while (i <= j) {
        while (i <= j && arr[i] <= pivot) {
            i++;
        }
        while (i <= j && arr[j] > pivot) {
            j--;
        }
        if (i < j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    int temp = arr[left];
    arr[left] = arr[j];
    arr[j] = temp;
    return j;
}

在上面的示例中,我们定义了一个快速排序的方法quickSort()和一个分区的方法partition()quickSort()方法接收一个一维数组和数组的左右边界,首先判断左边界是否小于右边界,如果小于,则选定数组的第一个元素为基准元素,使用partition()方法将数组分成两个部分,然后递归地对两个部分进行排序。partition()方法接收一个一维数组和数组的左右边界,首先选定数组的第一个元素为基准元素,使用两个指针i和j从左右两端向中间扫描,将比基准元素小的元素交换到数组的左边,比基准元素大的元素交换到数组的右边。最后将基准元素交换到它正确的位置上,并返回它的下标。

以下是一个对数组arr进行快速排序的示例:

int[] arr = {5, 2, 8, 1, 4};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr)); // 输出 [1, 2, 4, 5, 8]

在上面的示例中,我们首先定义了一个包含元素5、2、8、1、4的一维数组arr,然后调用quickSort()方法对数组进行排序,最后输出排序后的数组,得到[1, 2, 4, 5, 8]。

快速排序的时间复杂度为O(nlogn),虽然它的实现比冒泡排序要复杂一些,但在处理大量数据时效率更高。

数组的遍历

数组的遍历是指按顺序访问数组中的每个元素。在Java中,可以使用for循环或foreach循环对数组进行遍历。

以下是使用for循环对数组进行遍历的示例:

int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; i++) {
    System.out.print(arr[i] + " ");
}
// 输出 1 2 3 4 5

在上面的示例中,我们首先定义了一个包含元素1、2、3、4、5的一维数组arr,然后使用for循环遍历整个数组,每次输出当前元素的值。

以下是使用foreach循环对数组进行遍历的示例:

int[] arr = {1, 2, 3, 4, 5};
for (int num : arr) {
    System.out.print(num + " ");
}
// 输出 1 2 3 4 5

在上面的示例中,我们首先定义了一个包含元素1、2、3、4、5的一维数组arr,然后使用foreach循环遍历整个数组,每次输出当前元素的值。

无论是for循环还是foreach循环,对于一维数组的遍历都是非常简单的。

数组的复制

Java提供了多种方式对数组进行复制。其中,使用Arrays.copyOf()方法和System.arraycopy()方法是最常见的两种方式。

以下是使用Arrays.copyOf()方法对数组进行复制的示例:

int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = Arrays.copyOf(arr1, arr1.length);
System.out.println(Arrays.toString(arr2)); 

在上面的示例中,我们首先定义了一个包含元素1、2、3、4、5的一维数组arr1,然后使用Arrays.copyOf()方法将该数组复制到另一个数组arr2中,最后输出复制后的数组arr2

以下是使用System.arraycopy()方法对数组进行复制的示例:

int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[arr1.length];
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
System.out.println(Arrays.toString(arr2)); // 输出 [1, 2, 3, 4, 5]

在上面的示例中,我们首先定义了一个包含元素1、2、3、4、5的一维数组arr1,然后创建了一个长度与arr1相同的一维数组arr2,最后使用System.arraycopy()方法将arr1中的所有元素复制到arr2中,最后输出复制后的数组arr2

无论是Arrays.copyOf()方法还是System.arraycopy()方法,它们都可以方便地对数组进行复制,从而在编写代码时提高了效率。

数组的扩容和缩容

在Java中,一维数组的长度是不可变的,一旦定义了数组的长度,就无法再改变它。如果需要在运行时增加或减少数组的长度,可以使用其他的数据结构,例如ArrayList。

对于需要频繁进行扩容或缩容操作的情况,ArrayList是比较适合的一种数据结构。但是,如果只需要偶尔进行扩容或缩容操作,也可以考虑使用新数组来代替旧数组,从而实现数组的扩容和缩容。

以下是一个数组扩容的示例:

int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[arr1.length + 1];
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
arr2[arr2.length - 1] = 6;
System.out.println(Arrays.toString(arr2)); // 输出 [1, 2, 3, 4, 5, 6]

在上面的示例中,我们首先定义了一个包含元素1、2、3、4、5的一维数组arr1,然后创建了一个长度比arr1大1的一维数组arr2,使用System.arraycopy()方法将arr1中的所有元素复制到arr2中,最后将新元素6添加到arr2的最后一个位置。最终输出扩容后的数组arr2

数组缩容的实现方式与数组扩容类似,只需要将新数组的长度设置为原数组的长度减1,然后使用System.arraycopy()方法将原数组的元素复制到新数组中即可。以下是一个数组缩容的示例:

int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[arr1.length - 1];
System.arraycopy(arr1, 0, arr2, 0, arr2.length);
System.out.println(Arrays.toString(arr2)); // 输出 [1, 2, 3, 4]

在上面的示例中,我们首先定义了一个包含元素1、2、3、4、5的一维数组arr1,然后创建了一个长度比arr1小1的一维数组arr2,使用System.arraycopy()方法将arr1中的前4个元素复制到arr2中,最后输出缩容后的数组arr2

需要注意的是,当进行数组扩容或缩容操作时,原数组中的元素可能会被拷贝到新数组中。如果原数组中的元素是对象类型,那么拷贝时实际上只是复制了对象的引用,而不是对象本身。因此,如果修改新数组中的某个元素,可能会影响原数组中相应元素的值。为了避免这种情况,可以使用Arrays.copyOf()方法或System.arraycopy()方法来创建新数组,并将原数组的元素复制到新数组中,这样就可以确保新数组中的元素不会影响原数组中的元素。

结论

本文介绍了Java数组在内存中的存储方式,以及数组的基本操作,包括访问数组元素、遍历数组、数组的复制、数组的扩容和缩容等。数组是Java中最基本的数据结构之一,它可以用于存储一组相关数据,并且可以方便地进行操作。在实际开发中,需要根据实际情况选择不同的数据结构,以便更好地实现所需的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/488702.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

美国访问学者博士后签证过期后应如何更新?

“美国访问学者类J签证过期后应如何更新&#xff1f;”有读者希望知识人网分享相关经验。今天我们来简单介绍下美国签证免面谈代传递服务&#xff0c;以及其申请条件和相关流程&#xff0c;希望能对大家有所帮助。 首先我们需要明确两个概念&#xff1a; DS2019表的有效期&…

第一章(四):Django框架的模板(DTL):变量、标签、模板过滤器、模板继承、配置模板文件(staticfiles)

系列文章目录 备注&#xff1a;这里是Django系列文章的所有文章的目录 第一章(一) : Django框架如何创建项目、创建应用、创建templates&#xff1b;如何启动django项目&#xff1b; 第一章(二)&#xff1a;Django框架的模式、路由、视图&#xff1b; 第一章(三)&#xff1a;D…

copilot平替tabnine解析

1.copilot 借着最近chatgpt大火的东风&#xff0c;copilot又重新火了一把。 什么是copilot&#xff1f;直接上wiki百科上的解释 GitHub Copilot是GitHub和OpenAI合作开发的一个人工智能工具&#xff0c;用户在使用Visual Studio Code、Microsoft Visual Studio、Vim、Cursor或…

Flask使用Flask-SQLAlchemy对数据库操作详解二(配置、表与表之间一对一、多对一、多对多关系及增删改查参数和代码详细总结)

前面接&#xff1a;Flask使用Flask-SQLAlchemy对数据库操作详解二&#xff08;配置、表与表之间一对一、多对一、多对多关系及增删改查参数和代码详细总结&#xff09; 文章目录 4.增删改查操作4.1添加1. 使用add()方法添加新数据2. 使用create()方法创建新数据 4.2 查询数据1.…

ADAS/AD笔记之特斯拉Autopilot-HW3.0系统

摘要&#xff1a; 目前国内一般直接将高速NOA成为“L2”&#xff1b;因此&#xff0c;复杂度更高的城区NOA&#xff0c;就自然的映射到了“L2”。 一、概述&#xff1a; 特斯拉Autopilot系统首创了NOA这种淡化脱手/脱眼/脱脑&#xff08;驾驶自动化程度&#xff09;逻辑的功能…

电脑系统怎么选?Win?MacOS?Linux?

马上要学编程了&#xff0c;我们要学什么操作系统呢&#xff1f;是MacOS&#xff0c;还是Windows&#xff0c;或者是Linux或者其他&#xff01;那我们今天就来说说MacOS系统和Windows系统的优缺点&#xff0c;也介绍一下其他的系统。让你心里有底&#xff01; 1、Windows 首先当…

OpenShift 4 - 在 CI/CD Pipeline 中创建 KubeVirt 容器虚拟机 - 方法3

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在支持 OpenShift 4.12 的 OpenShift 环境中验证 文章目录 创建并运行 CI/CD Pipeline访问 VMPipeline 的 Task 解读 创建并运行 CI/CD Pipeline 执行命令&#xff0c;生成公钥-私钥对。 $ ssh-keygen$ l…

贪心法——最小生成树

问题描述&#xff1a; 最小生成树Time Limit: 2000 MSMemory Limit: 5000 KB Description 给定n(n<500)个顶点,以及E(E<20000)条边&#xff0c;计算最小生成树的权值.Input 第一行输入T表示有T组数据。每组数据第一行输入n、E&#xff0c;分别表示顶点数和边数. 接下…

2023年度国家自然科学基金项目申请初审结果公布~

2023年4月28日&#xff0c;国家自然科学基金委员会发布了《关于公布2023年度国家自然科学基金项目申请初审结果的通告》&#xff0c;2023年度国家自然科学基金项目申请初审结果公布&#xff01;2023年度项目申请集中接收期间&#xff0c;自然科学基金委共接收各类型项目申请304…

8年测试经验分享,15K的测试工程师需要掌握那些知识?

软件测试行业是随着软件产业的发展而兴起的一个重要领域&#xff0c;目前处于快速发展阶段。以下是软件测试行业的现状&#xff1a; 人才需求增长&#xff1a;随着互联网、移动互联网、物联网等新技术的不断发展&#xff0c;软件测试人才需求呈现出快速增长的趋势。越来越多的…

找高清无水印视频素材,就上这9个网站。

推荐几个我的视频素材库&#xff0c;有免费、收费、商用&#xff0c;希望对大家有帮助&#xff01; 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx 菜鸟图库可以找到设计、办公、图片、视频、音频等各种素材。视频素材就有上千个&#xff0c;全部都很高清&…

20.考虑用户舒适度的冷热电多能互补综合能源系统优化调度

说明书 MATLAB代码&#xff1a;考虑用户舒适度的冷热电多能互补综合能源系统优化调度 关键词&#xff1a;用户舒适度 综合能源 PMV 优化调度 参考文档&#xff1a;《冷热电气多能互补的微能源网鲁棒优化调度》基础模型加舒适度部分模型&#xff1b; 仿真平台&#xff1a;MA…

IBM停止招聘可被AI取代的职位;三星禁止员工使用ChatGPT;印象笔记官方AI免费课;清华美院AI绘画的高质量分享 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 『IBM将停止招聘可被人工智能取代的职位』近8000名工人将被自动化取代 据彭博社报道&#xff0c;IBM首席执行官Arvind Krishna表示&am…

三、信号槽

三、信号槽 1.概念&#xff1a; 信号槽指的是信号函数与槽函数的连接&#xff0c;可以使用不同的对象通过信号槽连接在一起&#xff0c;从而实现对象之间的通信。 信号槽认为是对象之间的一种约定&#xff1a;如果.......&#xff0c;则....... 信号槽是Qt新增的特性&#xff…

五一2.74亿人倾巢而出,小长假到底去哪玩儿?

今天是五一劳动节小长假结束后&#xff0c;打工人上班的第一天。 今年的五一非比寻常&#xff0c;是三年口罩之后的第一个小长假&#xff0c;回家探亲的回家、出门玩的出门&#xff0c;据统计&#xff0c;今年五一劳动节5天小长假&#xff0c;累计有2.74亿人次在国内出游&…

Redis-周阳(17. Redis 分布式锁)学习笔记

上一篇 &#xff1a; 16.短信验证码 文章目录 1. 相关面试题2. Redis 搭建3. 编码实现分布式锁3.1 建 Model3.2 改 POM3.3 写 YML3.4 主启动3.5 业务类3.6 小测试 4. 上述案例存在问题及修改4.1 没有加单机版的锁4.2 分布式部署之后&#xff0c;单机版的锁失效4.3 出现异常时&a…

使用iServer的Web打印功能打印临时图层tempLayersSet资源

作者&#xff1a;Carlo 背景&#xff1a;最近很多小伙伴想要了解如何将临时图层资源打印输出为图片&#xff1f;其实&#xff0c;官网已发布的10.2.1版本以及11.1.0版本的iServer就已经支持该功能&#xff0c;我们可以通过下文的介绍来学习使用。 步骤一、生成一个临时图层资源…

【C生万物】 结构体篇

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; &#x1f449; 专栏&#xff1a;《C生万物 | 先来学C》&#x1f448; 前言&#xff1a; 这期继续C语言的学习&#xff0c;进行结构体的讲解&#xff0c;值得注意的是&#xf…

Java版企业电子招标采购系统源代码Spring Boot + 二次开发 + 前后端分离 构建企业电子招采平台之立项流程图

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及…

SpringCloud------Eureka集群版整合生产(集群)以及消费(五)

SpringCloud------Eureka集群版整合生产以及消费&#xff08;五&#xff09; 集群避免单点故障的发生。 服务注册&#xff1a; 将服务信息注册进服务中心 服务发现&#xff1a; 从注册中心上获取服务信息 实质&#xff1a;存key服务名&#xff0c;取value调用地址 1.先启动eur…