Java—常用的数据结构—数组

理解数组

试想一下,如果编写一个程序,需要储存12个月份的天数,是否要定义12个变量呢?如果编写一个扑克程序,里面应该需要储存54张扑克的信息,是否要定义54个变量?而如果程序需要储存上成千上万的数据,程序员是不是也要逐一定义成千上万个变量?如果这样做一是工程量太大,而是这些逐一定义的变量间彼此独立,没有任何内在联系,这会对维护这些变量带来巨大困难,有时甚至无法处理。为了解决这个问题,聪明的程序设计者创造了数组这个好用的数据结构。数组概念的引入,大大方便了程序的设计,如下图所示。
在这里插入图片描述
数组(Array),顾名思义就是一组数据。当然这“一组数据”得是有一定关系的数据,否则只会使问题更复杂,下面我们就开始学习数组。

在Java中,数组也可以视为一种数据类型。它本身是一种引用类型,引用数据类型我会在后期的文章中详细介绍,这里仅仅给大家讲解一个基本的概念。

引用类型(reference type)引用类型的变量非常类似于C/C++的指针。而指针就是变量在内存中的址。任何变量只要存在于内存中,就需要有个唯一的编号标识这个变量在内存中的位置,而这个唯一的内存编号就是内存地址,它就是所谓的指针(pointer)。指针变量里存储的内容,可以想象成宾馆里的门牌号,或者居民的身份证号码。这些指针是为了我们访问(或定位)某个事物(或变量)而发明的一种机制。找到了门牌号,就能找到房间。知道了某个人的身份证号,也就找到其对应的人。类似地,找到了内存地址,也就可以找到地址所对应的变量。

现实生活中,我们比较容易记住一个人的姓名,却难以记住一个人的身份证号码。同样,对于一个32位或64位的内存地址,我们也难以记忆,也容易出错,为了方便操作,给这个32位或64位的地址取一个好记的名称,这个名称在C/C++中,就叫指针变量。而放到Java中,就叫引用类型变量。通过现象看本质,它们二者在哲学的地位是对等,但处理的细节上,这两种语言还是有所区别的。在讲这些区别之前,让我们回顾一下影星周星驰主演的电影《唐伯虎点秋香》里的一个有趣的桥段:

才子唐伯虎化名“华安”,在华府做一个仆人,试图创造条件追求华府丫鬟秋香。而华府的大管家武状元对唐伯虎分配了一个终身代号—— 9527。

假设“武状元”想找到“唐伯虎”,利用C/C++的机制,他即可以用代号“9527”(相当于指针)找到“唐伯虎”,他也可以用“华安”找到唐伯虎。因为“华安”就是“唐伯虎”的别名,找到“华安”,就是找到“唐伯虎”。因为C++中,引用的概念就是某个变量的别名(alias)。

而利用Java的机制,“武状元”想找到“唐伯虎”,他需要利用“华安”或者“唐伯虎”这两个引用名称,这里Java中的“引用”和C++中“引用”是有不同的,Java中的“引用”数据类型更像C/C++中的地址指针,所不同的是,Java为了方便用户,做了二次包装。当“武状元”喊的是“华安”或“唐伯虎”,实际上Java会内部把这两个名称转换成“9527”,只不过这个内部转换为用户是“透明”的罢了。如下图所示。
在这里插入图片描述
现在回到Java数组的概念上来。Java的数组既可以存储基本类型的数据,也可以存储引用数据类型的数据。例如int是一个基本类型,但int[](把“int[]”当成一个整体)就是一种引用数据类型。下面简明描述了这两种变量定义的方式,二者在地位上是对等的。
在这里插入图片描述
int[]就是一种数据类型,与int、float等基本数据类型类似,同样可以使用该类型来定义变量,也可以使用该类型进行类型转换等。在使用int[]类型来定义变量、进行类型转换时,与使用其他基本数据类型的使用方式没有任何区别。

我们先来通过一个简单的例子来感性认识一下数组。例如,我们需要存储12个月份的天数

一维数组的使用(ArrayDemo8 1.java)。
在这里插入图片描述

在这里插入图片描述
第07行定义了一个整型数组month,并使用12个月份的天数初始化。注意,从C\C++过渡到Java的初学者需要注意的是,在C和C++中定义数组的格式是

在这里插入图片描述
这种格式也可以获得完全一致的结果。然而,在Java中,最“通用”的方式还是范例中的语法,因为它指出类型是“一个 int 数组”。另外,在java中
在这里插入图片描述
的写法是错误的,在定义数组时不能在方括号中写下数组的长度,这点尤其需要初学者注意。

第11~15行,利用for循环输出数组的内容。在Java中取得数组的长度(也就是数组元素的个数)可以利用“.length”完成。在Java中,一切皆为对象,事实上,length是数组对象的一个属性,可以通过点操作符“.”来访问

因此,若要取得ArrayDemo8_1中所定义的数组month的元素个数,只要在数组month的名称后面加上“.length”即可,如下面的程序片段。
在这里插入图片描述
另外数组是从0开始索引的。也就是说,数组month的第一个元素是
在这里插入图片描述
或许大家已经注意到第14行的输出语句中有 “( i + 1 )” 这样的语句, 为什么需要括号呢?去了括号又会怎样?这是个简单而又有趣的小问题,请大家自行验证并思考原因。

在程序第07行中,
在这里插入图片描述
称为静态初始化,在声明的同时给出初始数据。但这样还是比较麻烦,回到扑克牌游戏,难道我们需要这样写54个数据来初始化扑克牌游戏?那对更大维度的数组的初始化有没有简便的方法?

下面我们来学习数组更加灵活的使用方法。

一维数组

我们可以给数组一个“定义”:数组是有序数据的集合,数组中的每个元素必须是相同的数据类型,可以用一个统一的数组名和下标来唯一地确定数组中的元素。

一维数组可以存放上千万个数据,并且这些数据的类型是完全相同的。

一维数组的声明与内存的分配

要使用Java的数组,必须经过以下两个步骤。

⑴ 声明数组。

⑵ 分配内存给该数组。

这两个步骤的语法如下。
在这里插入图片描述
在数组的声明格式里,“数据类型”是声明数组元素的数据类型,常见的类型有整型、浮点型与字符型等。“数组名”是用来统一这组相同数据类型的元素的名称,其命名规则和变量相同,建议大家使用有意义的名称为数组命名。数组声明后,接下来便要配置数组所需的内存,其中“个数”是告诉编译器,所声明的数组要存放多少个元素,而关键字“new”则是命令编译器根据括号里的个数,在内存中分配一块内存供该数组使用。例如。
在这里插入图片描述
例子中的第1行,当声明一个整型数组score时,可将score视为数组类型的变量,此时这个变量并没有包含任何内容,编译器仅会分配一块内存给它,用来保存指向数组实体的地址,如下图所示。
在这里插入图片描述
声明之后,接着要进行内存分配的操作,也就是例子中的第2行语句。这一行会开辟3个可供保存整数的内存空间,并把此内存空间的参考地址赋给score变量。其内存分配的流程如下图所示。
在这里插入图片描述
图中的内存参考地址0x1000一个假设值,此值会因环境的不同而异。数组属于非基本数据类型因此数组变量score所保存的并非是数组的实体,而是数组实体的参考地址。

除了用两行来声明并分配内存给数组之外,也可以用较为简洁的方式,把两行缩成一行来编写,其格式如下。
在这里插入图片描述
上述的格式会在声明的同时,即分配一块内存空间供该数组使用。下面的例子是声明整型数组score,并开辟可以保存11个整数的内存给score变量。
在这里插入图片描述
在Java中,由于整数数据类型所占用的空间为4个bytes,而整型数组score可保存的元素有11个,所以例子中占用的内存共有4 * 11 = 44个字节。下图是将数组score用图形来表示,从中可以比较容易理解数组的保存方式。
在这里插入图片描述

数组中元素的表示方法

想要使用数组里的元素,可以利用索引来完成。Java的数组索引编号从0开始,以一个名为score长度为11的整形数组为例,score[0]代表第1个元素,score[1]代表第2个元素,依此类推, score[10]为数组中的第11个元素(也就是最后一个元素)。下图为score数组中元素的表示及排列方式。
在这里插入图片描述
接下来看一个范例。在下面的程序里声明了一个一维数组,其长度为3,利用for循环输出数组的元素个数后,再输出数组的个数。

一维数组的使用(createArrayDemo8 2.java)
在这里插入图片描述
在这里插入图片描述
第6行声明整型数组a,并将空值null赋给a。

第7行开辟了一块内存空间,以供整型数组a使用,其元素个数为3。

第9行输出数组的长度。此例中数组的长度是3,即代表数组元素的个数为3。

第10~13行,利用for循环输出数组的内容。由于程序中并未对数组元素赋值,因此输出的结果都是0。也就是说整形数组中的数据默认为零。

数组元素的赋值

静态初始化在之前已经介绍过了,只要在数组的声明格式后面再加上初值的赋值即可,如下面的格式。
在这里插入图片描述
一维数组的赋值(arrayAssignment8 3.java)
在这里插入图片描述
在这里插入图片描述
第02行,将java.util包中的Random类导入到当前文件,这个类的作用产生为随机数。导入之后,在程序中才可以创建这个类以及调用类中的方法和对象(如第07行的rand对象)。

第07行,创建了一个Random类型的对象rand,Random对象可以更加灵活的产生随机数。

第10行,为数组a开辟内存空间,数组的长度为0~10(包含0,不包含10)的随机数。rand. nextInt(10) 返回一个 [0,10)区间的随机整型数。nextInt()是类型Random中产生随机整数的一个方法。

第16行为数组元素赋值,同样使用的是使用Random中的nextInt()产生随机数,所不同的是,随机整数的取值区间是[0,100)。

第17行接着输出数组元素。

程序稍微修改就可以得到ArrayAssignment2.java:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
是不是有点出乎意料?考虑一下这意味着什么?

事实上,这里最关键的一行代码就是第12行:“b = a;”,这行代码的含义是将a数组的引用赋值给数组b。这时a和b指向的数组对象是相同的。换句话说,此时的a和b是“一套数组,两套名字”,这就是Java中广泛使用的概念——引用(reference)。大家暂时不懂没有关系,但至少请记住这个现象,以后我们会用到这个特性。相关的知识我会在后期文章中详细讲解。

数组应用范例

从前面的范例可知,数组的索引就好像宾馆里的房间编号一样,想要找到某个房间,就得先找到房间编号!接下来再举一个例子,说明如何将数组里的最大值及最小值列出。

求数组中的最大值和最小值(arrayMaxMin8 4.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第06行声明整型数组a,其数组元素有5个,其值分别为74、48、30、17、62。

第7行和第8行分别声明了存放最大值的变量max和存放最小值的变量min,同时将min与max的初值均设为数组的第1个元素。

第12~19行逐一输出数组里的内容,并判断数组里的最大值与最小值。

第21~22行输出比较后的最大值与最小值。

将变量min与max初值设成数组的第1个元素后,再逐一与数组中的各元素相比。比min小,就将该元素的值指定给min存放,使min的内容保持最小;同样,当该元素比max大时,就将该元素的值指定给max存放,使max的内容保持最大。for循环执行完成,也就表示数组中所有的元素都已经比较完毕,此时变量min与max的内容就是最小值与最大值。

二维数组

二维数组的声明与分配内存

二维数组声明的方式和一维数组类似,内存的分配也一样是用new这个关键字。其声明与分配内存的格式如下。
在这里插入图片描述
同样,可以用较为简洁的方式来声明数组,其格式如下。
在这里插入图片描述
如果想直接在声明时就对数组赋初值,可以利用大括号完成。只要在数组的声明格式后面再加上所赋的初值即可,如下面的格式。
在这里插入图片描述
需要特别注意的是,用户不需要定义数组的长度,因此在数组名后面的中括号里不必填入任何的内容。此外,在大括号内还有几组大括号,每组大括号内的初值会依序指定给数组的第0、1、…、n行元素。下面是关于数组num声明及赋初值的例子。
在这里插入图片描述
语句中声明了一个整型数组num,数组有2行4列共8个元素,大括号里的几组初值会分别依序指定给各行里的元素存放,num[0][0]为23,num[0][1]为45,…,num[1][3]为28。

1. 每行的元素个数不同的二维数组

值得一提的是,Java在定义二维数组时更加灵活,允许二维数组中每行的元素个数均不相同,这点与其他编程程序语言不同。例如,下面的语句是声明整型数组num并赋初值,而初值的赋值指明了num具有三行元素,其中第1行有4个元素,第2行有3个元素,第3行则有5个元素。
在这里插入图片描述
下面的语句是声明整形数组num并分配空间,其中第1行有4个元素,第2行有3个元素,第3行则有5个元素。
在这里插入图片描述
上述定义的二位数组num的内存分布图如下所示。
在这里插入图片描述

2. 取得二维数组的行数与特定行的元素的个数

在二维数组中,若想取得整个数组的行数,或者是某行元素的个数,则可利用“.length”来获取。其语法如下。
在这里插入图片描述
也就是说,如要取得二维数组的行数,只要用数组名加上“.length”即可;如要取得数组中特定行的元素的个数,则须在数组名后面加上该行的索引值,再加上“.length”,如下面的程序片段。
在这里插入图片描述

二维数组元素的引用及访问

二维数组元素的输入与输出方式与一维数组相同,看看下面这个范例。

二维数组的静态赋值(twoDimensionArray8 5.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第6行声明整数变量sum用来存放所有数组元素值的和,也就是总成绩。

第7行声明一整型数组num,并对数组元素赋初值,该整型数组共有8个元素。

第12~21行输出数组里各元素的内容,并进行成绩汇总。

第22行输出sum的结果,即总成绩。

多维数组

要想提高数组的维数,只要在声明数组的时候将索引与中括号再加一组即可,所以三维数组的声明为int[][][] A,而四维数组为int[][][][] A … …,依此类推。

三维数组的使用方法(threeDimensionArray8 6.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于使用的是三维数组,所以嵌套for循环有3层。

1. Java中的null的使用

Java中变量通常遵循一个原则:先定义,并初始化后,然后再使用。有时候,我们定义一个类型变量,在刚开始的时候,无法给出一个明确的值,就可以用一个null来代替。

但是有一点需要注意的是,不可以将null赋给基本类型变量(如int、float、double等)。

比如,
A
是错误的。
在这里插入图片描述
是正确的,这里Object是一个class类型。

2. 数组的下标

在使用数组时,大家需要注意的是,人们的直观感觉计数一般是从1开始的,而Java中数组的下标是从0开始计数的。

此外,数组的下标不能超过(length-1),否则会产生越界错误,例如,我们定义个一个包含10个元素的数组score。
在这里插入图片描述
那么score[9]是我们能使用的最大下标数组元素,而score[10]则产生了越界。

Java—面向对象设计—类和对象

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值