这一次,我就给大家介绍一下数组的定义和应用。 话不多说,正文开始!

一、数组的基本用法

1.什么是数组

数组本质上允许我们“批量”创建相同类型的变量。

例如:

如果需要表示两个数据,可以直接创建两个变量int a; 整数

java初始化数组_数组初始化为null_java字符串数组和int数组

如果需要表示五个数据,可以创建五个变量int a1; 诠释a2; 诠释 a3; 诠释 a4; 诠释 a5;

java初始化数组_java字符串数组和int数组_数组初始化为null

但是如果你需要表示 10,000 个数据,那么你就不能创建 10,000 个变量。 这时候就需要用一个数组来帮我们批量创建。

数组初始化为null_java字符串数组和int数组_java初始化数组

上面是一个整数数组。

注意:在 Java 中,数组中包含的变量必须是同一类型。

2.创建数组

基本语法

java字符串数组和int数组_java初始化数组_数组初始化为null

在上面的例子中,我们以整型数组为例,创建了一个int[]类型的array数组,存储了5个整型数据。 讲解了数组创建的用法,Java数组的创建和C语言很相似,但是还是有区别的。

C语言版本数组的创建:

java字符串数组和int数组_数组初始化为null_java初始化数组

Java版本数组的创建:

数组初始化为null_java初始化数组_java字符串数组和int数组

我们可以通过两种写法看出区别。 在 Java 中,[ ] 必须紧挨着数据类型。

数组的数据在内存中连续存储。 继续以上面的代码为例:

数组的每个元素都有其对应的下标,下标->从0开始。如果我们要在这个数组中查找某个数据,就需要通过数组的下标来访问。

注意:数组也称为集合,存储一组相同类型的数据! ! 下标从位置 0 开始。

3.定义数组的方式

定义一

java字符串数组和int数组_java初始化数组_数组初始化为null

我们上面写的代码定义并初始化了一个数组。

java字符串数组和int数组_数组初始化为null_java初始化数组

这种方式只定义了一个数组。 这是我们定义数组的第一种方式。 这样定义的数组默认大小为0。

定义方法二

数组初始化为null_java初始化数组_java字符串数组和int数组

这样也定义了一个数组,但是这个数组的定义是通过关键字new给数组分配了一块内存,而这块内存可以存放10个数据。

数组初始化为null_java初始化数组_java字符串数组和int数组

int [ 10 ] 分配了连续的内存空间。

但是此时我们还没有对array数组进行初始化,所以此时array中十个数据的默认值为0。

定义方法三

java初始化数组_数组初始化为null_java字符串数组和int数组

在Java中,初始化后,=左边的[]不能填数字,=右边的[]只能在第二种定义方式中赋值。

在第三个定义

数组初始化为null_java初始化数组_java字符串数组和int数组

总结一下定义数组的三种方式:

数组初始化为null_java字符串数组和int数组_java初始化数组

在这三种方式中,Java中定义数组最常用的方式是方式一。

4.数组的使用

(1) 获取长度

防范措施

使用 arr.length 获取数组的长度。 这个操作是一个成员访问操作符,后面在面向对象中会经常用到。

代码示例:

java初始化数组_java字符串数组和int数组_数组初始化为null

编译结果:

java字符串数组和int数组_java初始化数组_数组初始化为null

(2)访问数组中的元素

如何访问数组:

数组初始化为null_java初始化数组_java字符串数组和int数组

防范措施:

1. 使用 [ ] 选择数组元素。 需要注意的是下标从0开始计数

2、使用[ ]操作不仅可以读取数据,还可以修改数据。

3、下标访问操作不能超过有效范围[0,length),如果超过有效范围,会出现下标越界异常

代码示例:

java字符串数组和int数组_java初始化数组_数组初始化为null

编译结果:

java字符串数组和int数组_数组初始化为null_java初始化数组

(3)下标越界

数组下标从0开始,取值范围为[0, arr.length),左闭右开区间,即[0, arr.length-1]。

如果我们下标数组边​​界之外的值…

如下

java初始化数组_java字符串数组和int数组_数组初始化为null

编译结果:

数组初始化为null_java初始化数组_java字符串数组和int数组

(4)遍历数组

所谓“遍历”是指访问数组中的所有元素,不重复不遗漏。 通常需要搭配循环语句

1.遍历方法(一)—–for循环

java初始化数组_数组初始化为null_java字符串数组和int数组

编译结果:

java初始化数组_java字符串数组和int数组_数组初始化为null

我们可以看到,数组中的元素是用for循环一个一个遍历并打印出来的。

2.遍历方式(2)—->for-each

for-each 是使用 for 循环的另一种方式。 可以更轻松的完成数组的遍历。 它可以避免循环条件和更新语句。

for-each 的基本用法

java字符串数组和int数组_java初始化数组_数组初始化为null

代码示例:

数组初始化为null_java初始化数组_java字符串数组和int数组

编译结果:

java字符串数组和int数组_java初始化数组_数组初始化为null

for-each遍历的原理

遍历数组中的每一个元素,取出每一个元素,然后赋值给x,最后打印x,直到遍历完数组中的所有元素。

两种遍历方式我们介绍完了,那么for循环和for-each有什么区别呢?

for循环可以得到数组下标,但是for-each不能得到数组下标java初始化数组,所以for-each只能遍历所有,不能对数组元素进行修改和操作。

3.遍历方法(3)——使用工具类操作数组打印数组

Arrays是一个用于操作Java数组的工具类。 如果你想用一个数组做一些事情,你可以通过它来完成。 当然,有些事情是它做不到的。

比如:我们要打印一个数组,我们本来是用for循环或者for-​​each来写的,但是我们也可以使用Arrays工具类来打印。

通过JDK的工具文档,我们找到了对应的工具类。

java字符串数组和int数组_数组初始化为null_java初始化数组

数组初始化为null_java初始化数组_java字符串数组和int数组

好吧,我们对arr数组进行如下操作:

java初始化数组_数组初始化为null_java字符串数组和int数组

编辑结果:

java字符串数组和int数组_数组初始化为null_java初始化数组

5.数组在内存中的存储

我们在之前的博客中简单介绍过Java中的内存区域划分,那么今天我们就知道了数组的引用类型,那么它是如何存储在内存中的呢?

下面简单回顾一下Java的内存区域

java初始化数组_数组初始化为null_java字符串数组和int数组

我们知道局部变量存放在Java虚拟机栈中,而数组数据存放在堆中。 数组的数据在堆上有一个特定的地址,数组的变量实际存放的是这组数据的地址。 栈上的这个变量根据这个地址找到堆上的数据。

数组的具体存储如下图所示:

java字符串数组和int数组_数组初始化为null_java初始化数组

当心:

数组初始化为null_java字符串数组和int数组_java初始化数组

数组初始化为null_java初始化数组_java字符串数组和int数组

数组初始化为null_java字符串数组和int数组_java初始化数组

上图是arr指向的数据在堆中的地址。 这个地址不是真实地址,是通过官方地址哈希得到的。 但是我们可以把它当作一个真实的地址,因为这个地址也是唯一的。

那么为什么需要对真实地址进行哈希处理呢?

这就是Java的安全性,自身数据的地址不会轻易暴露。

二、数组作为方法的参数

一、基本用法

代码示例:打印数组内容

数组初始化为null_java字符串数组和int数组_java初始化数组

在这段代码中

1.int[ ] a是函数的形参,int[ ] arr是函数的实参。

2.如果需要获取数组的长度,也可以使用a.length

2.理解引用类型

在上一篇博客的方法使用中,我们介绍了一个使用方法交换两个变量的具体案例。 现在让我们回顾一下。

(1)参数传递内置类型

java字符串数组和int数组_数组初始化为null_java初始化数组

我们使用内置类型作为参数来交换变量,但是在最终的编译结果中这两个变量并没有交换。

为什么是这样?

数组初始化为null_java字符串数组和int数组_java初始化数组

在不影响实参值的情况下交换形参的值。

(2)参数传递数组类型

java初始化数组_java字符串数组和int数组_数组初始化为null

我们使用数组作为参数来交换变量。 编译运行后,我们发现两个变量的值已经成功交换了。 这时候数组名arr就是一个“引用”。 传递参数时,参数通过引用传递。

那么为什么传引用类型既可以有形参又可以对实参进行操作呢?

在这里,我们将从内存开始。 上面我们介绍了数组在内存中的存储。

数组初始化为null_java字符串数组和int数组_java初始化数组

我们可以知道,栈中存储的变量实际上存储的是数据在堆中的地址。 当我们将arr数组作为参数传递给方法时,我们传入的是数据在堆中的地址。 在方法内部,我们可以根据这个地址找到堆中的数据,然后修改数据,从而实现形参改变实参的操作。

java字符串数组和int数组_java初始化数组_数组初始化为null

总结:

所谓“引用”,本质上只是存储了一个地址。 Java 将数组设置为引用类型。 这样,后面的数组参数传递实际上只是将数组的地址传递给函数参数。 这样可以避免整个数组的Copy(数组可能比较长,所以copy开销会很大)。

3.识别null

引用类型的 0 值为 null。

java字符串数组和int数组_java初始化数组_数组初始化为null

当我们将 null 分配给引用类型 arr 时,这意味着什么?

表示arr的引用,不指向任何对象。

当我们运行这段代码时,显示的结果是 null,而不是值 0。

数组初始化为null_java字符串数组和int数组_java初始化数组

那我们再看一个问题,当我们给arr赋null的时候,arr数组的长度是多少?

我们猜测它可能是 0,现在让我们运行代码。

运行结果如下:

数组初始化为null_java字符串数组和int数组_java初始化数组

此时小编报错,错误类型:空指针异常。

好了,到这里我们就可以知道,null被赋给了arr,而arr并没有指向任何数组对象,堆上也没有分配内存空间,所以我们无法找到它的长度。

总结:

无效的。 任何事情,都会出现空指针异常错误。

经验:以后只要出现这样的异常,就一定是this reference为null。

4.初识JVM内存区域划分

一栋宿舍楼会分成几个不同的区域:大一、大二……计算机专业、通信专业……

记忆也差不多,这条宽阔的走廊被分成了很多部分,每个区域存储着不同的数据。

JVM的内存分为几个区域,如图:

java字符串数组和int数组_java初始化数组_数组初始化为null

程序计数器(PC寄存器):

它只是一个小空间,用于保存下一条要执行的指令的地址。

虚拟机堆栈(JVM Stack):

重点是存放局部变量表(当然还有其他信息)。 我们刚刚创建的int[] arr的存储地址的引用就存储在这里。

本机方法堆栈

本地方法栈的作用类似于虚拟机栈。 只有保存的内容才是Native方法的局部变量。 在某些版本的 JVM 实现中(如 HotSpot),本地方法栈和虚拟机栈是在一起的。

堆:

JVM 管理的最大内存区域。 使用new创建的对象存放在堆上(比如之前的new int[]{1, 2, 3})

方法区:

用于存放虚拟机已经加载的类信息、常量、静态变量、just-in-time编译器编译后的代码等数据。 方法编译后的字节码存放在这个区域。

运行时常量池:

它是方法区的一部分,存储文字(字符串常量)和符号引用。 (注意从JDK1.7开始,运行时常量池在堆上)

关于上面的划分方式,我们会在后面的学习中慢慢了解。 这里重点了解虚拟机栈和堆。

java初始化数组_java字符串数组和int数组_数组初始化为null

1.局部变量和引用存放在栈中,新对象存放在堆中。

2.堆空间很大,栈空间比较小。

3、堆是整个JVM共享的,每个线程都有一个栈(一个Java程序中可能存在多个栈)。

三、数组作为方法的返回值

代码示例:

数组初始化为null_java字符串数组和int数组_java初始化数组

这段代码当然是可行的,但是它破坏了原来的数组。 有时候我们不想破坏原来的数组,就需要在方法内部新建一个数组,并且有方法返回。

修改后的代码:

java初始化数组_java字符串数组和int数组_数组初始化为null

这样原数组就不会被破坏。

另外,由于数组是引用类型,返回时只将数组首地址返回给函数调用者,并不复制数组内容,效率更高。

4.数组练习

1.数组转字符串

题目要求:

实现方法 toString 将整数数组转换为字符串。

比如数组{1, 2, 3},返回的字符串是“[1, 2, 3]”,注意逗号的位置和个数。

java初始化数组_数组初始化为null_java字符串数组和int数组

2.数组复制

如何复制数组

1.for循环复制

java初始化数组_数组初始化为null_java字符串数组和int数组

在这个copy方法中,我们先通过new新建一个与arr等长的数组copy,然后通过for循环将arr数组的内容逐一赋值给copy数组,达到最终的数组copy效果。

2.Arrays数组的工具类

1) 副本()

我们先通过JKD的工具文档看看copy工具的使用方法

数组初始化为null_java字符串数组和int数组_java初始化数组

功能:复制指定的数组,用零截断或填充(如果需要)以使副本具有指定的长度。

看一下Java中copyOf方法的具体实现

数组初始化为null_java字符串数组和int数组_java初始化数组

首先,Arrays.copyOf()的返回类型是int[],第一个参数是原数组(要复制的数组),第二个参数是新数组的长度(可以自己设置)。 如果新数组的长度大于 如果原数组长,则大于原数组长度的元素用0填充。具体如下…

数组初始化为null_java初始化数组_java字符串数组和int数组

(2) copyOfRange()

我们先通过JDK文档查看一下这个工具类的功能

java初始化数组_数组初始化为null_java字符串数组和int数组

功能:将指定数组的指定范围复制到一个新数组中。

看一下Java中copyof方法的具体实现

数组初始化为null_java初始化数组_java字符串数组和int数组

copyOfRange方法的返回类型是int[],第一个参数是原数组,第二个和第三个参数是要复制的原数组数据的下标,一定要记住是左闭右- 开区间,[ 从 , 到 )。

代码示例

java初始化数组_数组初始化为null_java字符串数组和int数组

3.系统。 阵列复制

java初始化数组_数组初始化为null_java字符串数组和int数组

我们打开System.arraycopy方法的具体实现,发现并没有上述copy方法的实现过程。 System.arraycopy是之前native中的native方法。

本机方法

1.在本地方法栈上运行

2.底层用C/C++代码实现

System.arraycopy 没有返回值。 第一个参数是原数组(要复制的数组),第二个参数是要复制的原数组的下标,第三个参数是目标数组,第四个参数是目标数组下标,第五个数组是要复制的长度。

代码示例:

数组初始化为null_java初始化数组_java字符串数组和int数组

当心:

System.arraycopy 的最后一个参数是要复制的数组的长度。 这个数据不能超过原数组的长度,否则编辑器会报错:数组越界。

4. Array name.clone —->生成当前数组的副本

功能:生成当前数组的副本。

代码示例:

数组初始化为null_java初始化数组_java字符串数组和int数组

3.找到数组中最大的元素

题目内容:给定一个整型数组,找出其中最大的元素(同理,寻找最小的元素)

代码:

java初始化数组_java字符串数组和int数组_数组初始化为null

4.求数组中元素的平均值

主题内容

给定一个整数数组,求平均值

代码:

java初始化数组_java字符串数组和int数组_数组初始化为null

当心:

最后,在aver方法中计算平均值的时候,一定要记住sum * 1.0,这样计算出来的平均值就是double类型的数据。

5.在数组中查找指定元素(顺序查找)

主题内容

给定一个数组和一个元素,找出该元素在数组中的位置。

代码

java初始化数组_数组初始化为null_java字符串数组和int数组

6.在数组中查找指定元素(二分查找)

对于排序数组,可以使用更高效的二分查找。

什么是有序数组?

顺序分为“升序”和“降序”。 比如1 2 3 4,按顺序递增就是升序。 比如4 3 2 1,降序就是降序。

以升序数组为例,二分查找的思路是先取中间位置的元素,看你要找的值是大于还是小于中间的元素。 如果比较小,往左边找; 否则,去右边找到它。

代码:

java初始化数组_数组初始化为null_java字符串数组和int数组

二分查找的具体思路可以参考我之前的一篇博客——在一个有序数组中查找具体的数字n(二分查找)详解。

7.检查数组的顺序

主题内容

给定一个整型数组,判断该数组是否有序(升序)

代码:

java初始化数组_数组初始化为null_java字符串数组和int数组

当心:

记得检查传入的数组是否为null,如果传入的arr数组为null,则返回false类型。

8.数组排序(冒泡排序)

主题内容

给定一个数组,按升序(降序)顺序对数组进行排序。

算法思路

每次它试图在当前区间中找到最小(或最大)的元素进行排序,并将其放在数组的前面(或最后)。

代码:

java字符串数组和int数组_数组初始化为null_java初始化数组

关于冒泡排序的详细讲解可以看我之前的博客——排序算法之冒泡排序,这里就不过多介绍了。

9.数组倒序

主题内容

给定一个数组,以相反的顺序对元素进行排序。

思路

设置两个下标,分别指向第一个元素和最后一个元素。 交换两个位置的元素。

然后让上一个下标自己增加,下一个下标自己减少,如此循环下去

代码:

java初始化数组_java字符串数组和int数组_数组初始化为null

五、二维数组

二维数组本质上是一维数组,只是每个元素都是一维数组。

正则二维数组

(1) 二维数组的定义

java字符串数组和int数组_java初始化数组_数组初始化为null

(2)内存中的二维数组

以上面的int[2][3]为例

java字符串数组和int数组_数组初始化为null_java初始化数组

数组初始化为null_java字符串数组和int数组_java初始化数组

前面我们说过,二维数组本质上是一种特殊的一维数组。

这个数组的每一行arr[0]和arr[1]构成一个一维数组,每一行存储指向每一列数据的地址。

每一列也是一个单独的一维数组,指向堆中的每一个数据。

(3) 二维数组的打印

我们知道二维数组在内存中的存储和方向,所以我们知道arr.length获取行数,arr[i].length获取列数。

for循环打印

我们用 for 循环打印这个二维数据。

代码示例:

java字符串数组和int数组_数组初始化为null_java初始化数组

打印结果:

数组初始化为null_java初始化数组_java字符串数组和int数组

2. for-each 打印

如果我们使用 for-each 打印,代码示例:

java字符串数组和int数组_数组初始化为null_java初始化数组

打印结果如下:

java初始化数组_数组初始化为null_java字符串数组和int数组

3.Arrays工具类打印

在一维数组中,我们希望使用 Arrays.toString() 将数组转换为字符串以进行打印。 那么二维数组转字符串的工具类是什么呢?

如果我们使用 Arrays.toString() 打印

数组初始化为null_java初始化数组_java字符串数组和int数组

结果打印 arr 行表示的数组的内容——列表示的一维数组的地址。 显然 Arrays.toString() 无法打印出二维数组的全部内容。

我们在 JDK 文档中找到了 deepToString() 工具类。

数组初始化为null_java初始化数组_java字符串数组和int数组

功能:返回指定数组的“深度内容”的字符串表示。

我们用 deepToString() 打印…

结果如下:

数组初始化为null_java字符串数组和int数组_java初始化数组

成功打印出二维数组的内容。

deepToString()可以正确打印二维数组的所有数据。

不规则二维数组

在C语言中,当我们定义一个二维数组时,我们可以只定义列而不指定行的值。

C语言数组的定义

java字符串数组和int数组_java初始化数组_数组初始化为null

在Java中java初始化数组,我们可以只定义行,列不需要指定值。

Java中不规则二维数组的定义

什么是不规则二维数组?

前面的常规二维数组,每一行的数据个数是一样的,列数也是一样的。 对于一个不规则的二维数组,行数是指定的,列数是自己定的,每行的列数是自己定的。

(1) 不规则二维数组的定义

java字符串数组和int数组_java初始化数组_数组初始化为null

编译结果:

java字符串数组和int数组_数组初始化为null_java初始化数组

首先我们指定一个有两行的二维数组

int [ ] [ ] arr = new int [2] [ ];

我们指定数组的每一行有多少列。

arr[ 0 ] = new int[ ] { 1,2,3 }

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

这就是不规则二维数组的定义。

(2)内存中的二维数组

数组初始化为null_java初始化数组_java字符串数组和int数组

与常规的二维数组内存存储基本相同。

(3)方式

同样,还有“三维数组”、“四维数组”等更复杂的数组,但出现的频率很低。

今天的分享到此结束,还请大家多多包涵,指点指点!