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

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中最基本的数据结构之一,它可以用于存储一组相关数据,并且可以方便地进行操作。在实际开发中,需要根据实际情况选择不同的数据结构,以便更好地实现所需的功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux中使用docker部署微服务

目录 一、制作jar包&#xff08;如果看一眼很简单&#xff0c;可以直接使用结尾的jar&#xff09; 1.首先创建一个微服务 demo2 2.启动微服务&#xff08;在DemoApplication上右键执行启动就行&#xff09; 注意&#xff1a;其他操作导致的 可能遇到的报错 3.修改端口 4.新…

超细Redis(一)

目录 概述 Redis是什么&#xff1f; Redis能干嘛&#xff1f; 特性 如何学习 Linux安装 测试性能 概述 Redis是什么&#xff1f; Redis &#xff08;Remote Dictionary Server&#xff09;,即远程字典服务 是一个开源使用ANSI C语言编写、支持网络、可基于内存亦可持…

【Java笔试强训 12】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;二进制插…

Python小姿势 - Python学习笔记——类与对象

Python学习笔记——类与对象 类与对象是面向对象编程的两个基本概念。类是对象的抽象概念&#xff0c;对象是类的具体表现。 类是对一类事物的抽象&#xff0c;它是描述一类事物的模板&#xff0c;而对象是类的具体表现。对象是类的实例&#xff0c;类是对象的模板。 举个例子&…

STM32 系列 DAC的介绍与使用

STM32网上资料多&#xff0c;对自己来说基本的使用也是很简单的&#xff0c; 我的STM32专栏并没有什么系统的基础教学&#xff0c;基本上是某个项目用到了&#xff0c;或者产品使用过程出过问题 才会来记录一下&#xff0c;正好用到了 DAC &#xff0c;一般产品还用得不多&…

QML应用动画(Applying Animations)

目录 一 扩展可点击图像元素版本2&#xff08;ClickableImage Version2&#xff09; 1 第一个火箭 2 第二个火箭 3 第三个火箭 动画可以通过以下几种方式来应用&#xff1a; 属性动画 - 在元素完整加载后自动运行&#xff1b; 属性动作 - 当属性值改变时自动运行&#xf…

【栈】的实现

&#x1f58a;作者 : D. Star. &#x1f4d8;专栏 : 数据结构 &#x1f606;今日分享 : —>&#x1f4d6;区块链 &#xff1a; 小明向你借100块钱&#xff0c;说一周后还你&#xff0c;然后你拿个喇叭大喊一声&#xff1a;我是某某&#xff0c;小明向我借了100块&#xff0c…

Vue3+Element Plus环境搭建和一键切换明暗主题的配置

Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。而Element Plus是一款基于Vue3面向设计师和开发者的组件库。 最终效果&#xff1a; 环境搭建 已安装 16.0 或更高版本的 Node.js&#xff0c;终端&#xff1a; npm init vuelatest这一…

Three.js--》Gsap动画库基本使用与原理

目录 Gsap动画库使用讲解 Gsap动画库基本使用 修改自适应画面及双击进入全屏 设置stats性能监视器 Gsap动画库使用讲解 GSAP的全名是GreenSock Animation Platform&#xff0c;是一个从flash时代一直发展到今天的专业动画库&#xff0c;今天将其与three.js进行结合&#x…

面试官:你知道 Spring lazy-init 懒加载的原理吗?

普通的bean的初始化是在容器启动初始化阶段执行的&#xff0c;而被lazy-init修饰的bean 则是在从容器里第一次进行context.getBean(“”)时进行触发。 Spring 启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefinition并存到Hashmap里供下面的初始…

HttpRunner3.x 源码解析(5)-runner.py

首先看下生成的pytest文件 from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCaseclass TestCaseLogin(HttpRunner):config (Config("登录成功").variables(**{"password": "tester", "expect_foo2": "co…

4.4.1内核编译

内核源码下载地址&#xff1a; https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.4.1.tar.gz 安装依赖包&#xff1a;报错就装 cp /boot/config-xxx ./.config make mrproper make menuconfig,然后save保存&#xff0c;退出 make -j4 //四线程编译 sudo ma…

Java基础(十六)泛型

1. 泛型概述 1.1 生活中的例子 举例1&#xff1a;中药店&#xff0c;每个抽屉外面贴着标签 举例2&#xff1a;超市购物架上很多瓶子&#xff0c;每个瓶子装的是什么&#xff0c;有标签 举例3&#xff1a;家庭厨房中&#xff1a; Java中的泛型&#xff0c;就类似于上述场景中的…

计算机视觉的应用4-目标检测任务:利用Faster R-cnn+Resnet50+FPN模型对目标进行预测

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用4-目标检测任务&#xff0c;利用Faster RcnnResnet50FPN模型对目标进行预测&#xff0c;目标检测是计算机视觉三大任务中应用较为广泛的&#xff0c;Faster R-CNN 是一个著名的目标检测网络&#x…

【Java校招面试】基础知识(六)——计算机网络

目录 前言一、TCP协议 / UDP协议二、HTTP协议后记 前言 本篇主要介绍计算机网络的相关内容。 “基础知识”是本专栏的第一个部分&#xff0c;本篇博文是第六篇博文&#xff0c;如有需要&#xff0c;可&#xff1a; 点击这里&#xff0c;返回本专栏的索引文章点击这里&#xf…

操作系统——操作系统逻辑结构

0.关注博主有更多知识 操作系统入门知识合集 目录 2.1操作系统的逻辑结构 思考题&#xff1a; 2.2CPU的态 思考题&#xff1a; 2.3中断机制 2.1操作系统的逻辑结构 操作系统的结构指的是操作系统的设计和实现思路&#xff0c;按照什么样的结构设计、实现。 操作系统的…

公司新来的00后真是卷王,工作没2年,跳槽到我们公司起薪18K都快接近我了

说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作都没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 …

通过Python的PIL库给图片添加图片水印

文章目录 前言一、素材准备1.原图2.水印图 二、使用PIL库给图片添加图片水印1.引入库2.定义图片路径3.打开原图4.打开水印图片5.计算水印图片大小6.计算原图大小7.调整水印图片大小7.1调整前7.2调整后 8.计算水印图片位置8.1左上8.2左下8.3右上8.4右下8.5中间 9.添加水印10.保存…

Doris(20):Doris的函数—数学函数

1 查看函数名 show builtin functions in test_db; 2 abs(double a) 功能: 返回参数的绝对值 返回类型:double类型 使用说明:使用该函数需要确保函数的返回值是整数。 3 acos(double a) 功能: 返回参数的反余弦值 返回类型:double类型 MySQL 中反余弦函数 ACOS(…

数据库基础及用户管理授权

数据库概念 关系型数据库 数据结构二维表格 库 -> 表 -> 列&#xff08;字段&#xff09;&#xff1a;用来描述对象的的一个属性&#xff1b;行&#xff1a;用来描述一个对象的信息 mysql&#xff08;5.7/8.0&#xff09; maridb ocracle postgresql sqlserver(windows…