二维数组详解

439 阅读21分钟

1、什么是二维数组?

二维数组本质上是以数组作为"数组元素"的数组。简单理解就是数组的数组

​ 从描述信息上理解的话,可能直接干懵逼了,为了理解这一个概念,我们不妨用图形的方式进行解释一下。

​ 首先我们看一下,一维数组的编写方式为 let ary = [1,2,3];,那么对应的图形化样式如下所示:

一维数组

​ 那么这个时候,如果有多个一维数组的话会变成什么样子呢?

多个一维数组

​ 不难看出来上面的一维数组非常的多,那么为了管理好多个一维数组,我们可以抽象的将数组a数组b数组c看成是一个元素,再组成一个数组的话,那么这样的结构就是二维数组

​ 如果用图形化的形式表达的话,如下所示。

二维数组

从上图可以看出,在二维数组里面a、b、c本身就是一个数组。那么这种方式组成了数组X就是一个二维数组

2、二维数组的语法规则

说到如何创建二维数组,那么我们就要了解一下二维数组的语法规则了。

# var ary =[[11,12,13],[21,22,23],[31,32,33]]

二维数组的语法规则

为了更好的理解语法,我们不妨对比一下一维数组二维数组的区别。

Snipaste_2020-08-30_04-48-39

打印出来看一下

Snipaste_2020-08-30_04-52-02

可以看出二维数组的特殊之处在于:二维数组的元素为“数组”

换一个表达形式就会是这个样子的。

Snipaste_2020-08-30_05-02-46

那么问题来了,我们如何表示元素内部的元素呢?

元素内部的元素,听起来是不是感觉很迷惑。我们还是通过画图的方式来进行理解。

Snipaste_2020-08-30_05-06-26

那么请问我们如何用二维数组的形式来表示出来。假设此时我们就是需要将上图中的31出来,我们怎么做呢?

Snipaste_2020-08-30_05-15-58

运行一下看看结果。

Snipaste_2020-08-30_05-20-24

那么此时我们要拿上图中的11,那么需要如何表示呢?

Snipaste_2020-08-30_05-25-26

我们只需要使用ary[0][0]的方式就能成功去出来,运行如下:

Snipaste_2020-08-30_05-30-12

从上面的案例可以看出,二维数组的使用方式其实就是在一维数组ary[ ]的基础上,再加一个[ ]

  • 二维数组的语法规则:ary[i][j]

  • i:第一个括号内代表的是最外层的元素

  • j:第二个括号内代表的是最内层的元素

# 细节:i和j都是从0开始来计算位置的。

如果将二维数组的内容都输出的话,就会是下面这个样子的。

Snipaste_2020-08-30_07-09-3812

通过上方手动的输出信息,那么就能够推导出二维数组中ary[i][j]的意义。

Snipaste_2020-08-30_07-09-3896

除了上面这种定义以外,我们还可以用另一种形式来理解二维数组的

Snipaste_2020-08-30_07-38-4012

比如我们使用ary[3][4],那么我们也可以表示为:定义一个ary为(3行4列)的数组

Snipaste_2020-08-30_08-02-41

3、二维数组的遍历

紧接着我们研究一下二维数据的相关操作。

在搞懂二维数组的遍历之前,我们首先回顾一下一维数组的遍历方式

Snipaste_2020-08-30_08-20-24

我们不妨将一维数组的遍历结果都打印出来看一下。

Snipaste_2020-08-30_08-26-18

那么我们的二维数组是如何遍历出来的呢?

Snipaste_2020-08-30_08-30-53

我们不妨打印出来看一下通过上方的方法遍历出什么结果?

Snipaste_2020-08-30_08-35-48

# 显然这种遍历方式还是不够的,我们还需要在考虑如何将里面的数组再遍历一次。

我们可以考虑在for循环内部再添加一个for循环,遍历出数组元素出来。

那么我们应该如何进行编写呢?

Snipaste_2020-08-30_09-09-36

然后在for循环里面,在创建一个for循环。这个for循环就比较有特点了。

Snipaste_2020-08-30_09-17-20

但是再创建一个for循环的时候,需要特别留意,里面的数组长度到底是指代的是什么?

Snipaste_2020-08-30_09-22-28

我们需要知道的是,第二个for循环目的是遍历最里面的数组。如下所示:

Snipaste_2020-08-30_09-27-581155444

需要注意的一点是,二维数组里面的元素的长度,并不是固定的,也就是说,并不是所有的二维数组都是和上图一样全都是长度一样的二维数组

其他的二维数组的数组元素长度可能不太一样。如下所示:

Snipaste_2020-08-30_09-27-58263

因此动态的计算里面的长度就非常重要了,不能直接编写死喽。具体的编写如下:

Snipaste_2020-08-30_09-43-50

这里再三强调一下ij分别代表的是什么?

Snipaste_2020-08-30_09-55-45

那么打印的时候,也会存在变化。

Snipaste_2020-08-30_09-59-19

那么我们看看打印出来的结果。

Snipaste_2020-08-30_10-03-40

# 你会发现,我们已经成功的将“二维数组”的数据,完整的遍历了出来。nice。

4、深入理解二维素组的遍历过程

为了深入理解双for循环的遍历过程,我们写一下过程,以便在后续的编写代码中,能心中有数。

Snipaste_2020-08-30_10-16-39

先执行外层for循环,然后进入内层for循环,直到内层for循环遍历结束,再跳出来执行外层for循环

Snipaste_2020-08-30_10-22-29

我们不妨模拟一下执行的过程。当i=0时,我们会遍历内层for循环

Snipaste_2020-08-30_10-31-1941521

当我们执行完内层的for循环以后,才会跳转到外层的for循环。

Snipaste_2020-08-30_10-47-53

体现出来就会是下面这个样子的。

Snipaste_2020-08-30_10-59-21

当遍历完内层for循环之后,那么再跳到i=1的情况。

Snipaste_2020-08-30_10-52-08

然后再次进入内层for循环,直到遍历完内层循环为止,然后再次挑出,再次执行完内层for循环,如此往复,直到所有都遍历完,然后结束。

我们其实从最终的打印效果就能够看出来。

Snipaste_2020-08-30_11-02-55

那么我们不妨总结一下,我们可以通过双for循环,可以按照顺序遍历出二维数组的元素。

Snipaste_2020-08-30_11-10-19

5、二维数组的执行顺序(拓展)

Snipaste_2020-08-31_05-47-44

我们从上面的演进过程中,能够知道的一些规律,我们总结一下。

# 我们遍历出来的数据,如果用'ary[i][y]'来表示元素内容的话,可以如下所示。

Snipaste_2020-08-30_12-05-088951

那么我们通过上面这些执行出来的结果可以用来干些什么呢?

​ 好像单纯的看这些输出的数据,并不能想象出有什么作用。但是我们在编写代码的时候,很多功能都是在这个输出的数据的基础上演变而来的。比如淘宝商品的规格值的选择就是经典的例子。

​ 能否做出复杂的功能,那么就需要你对二维数组的演变上有多了解了。那么接下来我们就开始深入的理解二维数组的其他知识点。了解这些知识点,那么对于日后开发来说可以说是非常的重要。

# 二维数组的别名:行列表

通常我们也会将二维数组称之为行列表,我们不妨举一个栗子,看看为什么二维数组叫做行列表呢?

日常生活中,我们经常能看到商家的格子铺。每一个格子都被区分开来,用于摆放艺术品。

格子铺

但是聪明的你也不难看出,“格子铺”其实也可以用进行区分。

格子铺行和列

我们完全可以将认为是数组,也可以将认为是数组。这样就成了行列矩阵

行数组和列数组

​ 通过行和列来存储的表(格子铺),就是典型的行列数组,从格子铺的形状可以看出,我们能将二维数组视作一个表,比如Excel表。因为Excel表就是按照行和列的方式进行储存的。

Snipaste_2020-08-29_13-09-50

​ 之所以引出表就是按照行和列的形式进行储存的这个结论。原因在于二维数组,其实也是可以通过这样的形式来进行表达。让二维数组可以按照表的形式进行储存。理解这一点,那么我们在复杂的编程逻辑中就有了空间想象能力。非常非常重要。

既然说到二维数组是可以用表的形式进行储存,那么该如何储存呢?

​ 其实这个和Excel表的储存方式非常的类似。我们不妨先看一下Excel表是如何储存的呢?

​ 在搞懂Excel表如何储存之前,我们可以看下Excel表的单元格是如何定位的。

Snipaste_2020-08-31_08-28-03

​ 接下来,我们让单元格储存数据“1”,然后在相同的位置再获取一下数据。

Snipaste_2020-08-31_08-51-51

​ 你会发现,单元格之所以能够储存上数据,且可以独立分开不受影响。是因为将数据按照行列坐标的方式进行储存。知道了这个结论之后,那么我们其实也是可以按照这个思路去模拟一个“二维数组”的表格。

下面这个是二维数组的行列表。

二维数组表

# 需要注意的是,上面这个表格,行和列都是按照数组的"索引"来进行排序的,相比较于Excel表格来说是不一样的。这点非常的重要,必须清楚这一点。
  之所以说是按照数组的“索引”来进行排序的,从双for循环中的遍历就能看出来。最终的效果就会如上面这张图片所示的那样,行是从0开始递增的,列也是从0开始递增的。

# 如何将数据填充到二维数组表中

那么我们就拿我们前面输出的数据来说,如何将数据填充到二维数据的表格中呢?

Snipaste_2020-08-30_12-05-088951

要想搞清楚如何二维数组的数据是如何排列的话,那么就还是需要我们之前提到的知识点。

Snipaste_2020-08-30_07-38-4012

​ 你会发现二维数组中的第一个括号的i指的是,第二个括号的j指的是。我们抓住这个特性,再来看上面的输出信息,那么我们就可以将数据按照这样的方式进行排版。

Snipaste_2020-08-31_08-59-239653

其实本质上就是行和列的定位,这一点和**Excel表格**的原理上是一致的。只要理解了这一点,那么就能够轻松的将数据存储在他指定的位置上去。

那么接下来我们看一下遍历后执行的顺序。

Snipaste_2020-08-30_12-05-088951-1598760703753

​ 那么我们把这个顺序体现在二维数组的表格内的话,会发生什么样的效果呢?

Snipaste_2020-08-31_08-59-23985

​ 那么接着我们将输出的数据也一并填充进行,那么我们会得到这么一个表格。

Snipaste_2020-08-31_08-59-23965123

# 如何在不画图的情况下快速定位

​ 在编程中,如果我们每一次都要通过上面的步骤一步一步的分析并且画图,那么这个过程就太麻烦了。

我们有没有方法可以快递的识别出来呢?

​ 答案是:可以的,我们是要在编程的时候,**对二维数组稍微改变一下位置,就能够快递的识别出上面的特性。**以便于快读的完成编程的工作。如果我们熟悉了这种方式甚至以后都不用在对二维数组进行改变位置就能看出效果。思维想象能力就更加突出。

Snipaste_2020-08-31_10-21-46

​ 我们只需要做一些小小的修改,那么就能够完成前面分析一大段的代码。

Snipaste_2020-08-31_10-25-54

# 你会发现我们仅仅是改变了编写的位置,并没有改变任何数据。

​ 你会惊讶的发现,执行的顺序就出来了,如果说你的空间想象能力比较强的话,你甚至能看出这里有一个二维数组的表格出来。

Snipaste_2020-08-31_10-32-14

# 那么我们只需要知道:“数据输出的规律是从左到右,一行一行的输出”的话,那么我们就可以在不画图的情况下,快速的辨别出里面的数据填充的顺序。

6、作死写法:行列互换

​ 为了进一步的榨干二维数组的知识点,我们思考一下,难道就只能得出从左到右,一行一行的输出这个结论吗?能不能从上到下,一列一列的进行输出呢?

# 放浪的程序员又开始作死啦。

# 数组元素长度相同的二维数组

要想实现这样的效果,其实也不难,但是为了方便理解,那么我们还是拿上面的二维数组进行举例。

# let ary = [[11,12,13],[21,22,23],[31,32,33]]

为什么一定要拿上面这个二维数组进行举例呢?

之所以这里强调拿的是上面这个二维数组的原因在于:这个二维数组内部的数据元素的长度都是相同的。这样方便分析以及容易编写代码。

Snipaste_2020-08-31_11-05-24

那么如何才能实现从上到下,一列一列的进行输出的效果呢?

重点在于下面这段代码,我们只需要将两个for循环进行内外互换,如下所示。

Snipaste_2020-08-31_11-15-44

# 互换一下,你会发现,我们是先执行一次“列”的外循环,然后再进入“行”的内循环。直到内循环结束,再退出执行外循环,如此往复,直到全部遍历位置。  细心的你会发现,其实这个就是和前面的案例完全相反。初步分析这种方式是可以达到我们想要的效果的。

那么我们不妨运行一下看看效果。

Snipaste_2020-08-31_11-24-43

# 咦,你会发现直接运行的时候,直接报错了,并且告诉我们第二十行代码出现了问题。

那么我们不妨到第二十一行代码中看一下。可以看出,在外层for循环中,直接使用i是不可以的。

Snipaste_2020-08-31_11-27-59

# 这种编写的方式也就代表了,这段代码不能用于“数组元素长度不相同”的二维数组。

最后编写成下面这个样子。

Snipaste_2020-08-31_11-38-29

我们运行看一下结果

Snipaste_2020-08-31_11-40-17

那么我们还是思考一下,这些结果是怎么被实现出来的。

这个时候,我们就可以对二维数组进行稍微的改动。

Snipaste_2020-08-31_11-43-45

然后再重新去查看双for循环的代码,其实你就能够分析出代码执行的顺序。

Snipaste_2020-09-05_10-35-03

运行的效果其实也可以看做是下面这个样子的。

Snipaste_2020-08-31_11-49-25

但是为了方便后续的操作,我们还是将数据填充到表格内。

Snipaste_2020-08-31_08-59-2653180

再填充一下顺序。

Snipaste_2020-08-31_08-59-26531

运行到这里,我们能够得出什么结论呢?

结论一、使用行列互换的for循环,遍历出来的数据执行顺序是'从上到下,一列一列的进行输出'
结论二、两组for循环,在二维数组表格内,最终输出的效果都是一样的。不同的是输出的效果顺序不一样。

注意:其实这种双for循环,存在致命的不足,那就是只能用于,数组元素长度都相同的二维数组。

# 数组元素长度不同的二维数组

为了验证数组元素不同的二维数组不适合行列互换,我们举下面这个例子。

假设我们第一个数组元素的长度改变一下,那么会输出什么样的效果呢?

Snipaste_2020-09-05_11-10-05

然后打印看看效果,你会发现打印出了两个**“undefined”**。

Snipaste_2020-09-05_11-09-49

其实,造成这样的原因在于,下面这两个地方也被遍历到了。

Snipaste_2020-09-05_11-27-30

再比如我们在第二个数组元素中,加入一个元素。

Snipaste_2020-08-31_12-03-48

运行一下,你会发现,有些数据会导致没有遍历成功。

Snipaste_2020-08-31_12-09-14

其实从打印的结果端看的话,还不一定能看出来,其实本质上是一阵列都没有被遍历出来。

Snipaste_2020-09-05_11-33-24

​ 究其原因,其实就是因为下面这段代码所致。我们在列的for循环中,使用的是固定的遍历方式,导致双for循环只能用于**“数组元素长度相同的二维数组”**。当数组元素长度不相同的时候,这种二维数组并不能实现我们想要的效果。

Snipaste_2020-09-05_11-38-03

# 但是这种代码的探讨,并不是就已经失去了意义,在编写"淘宝商品规格值"的选择功能上,就扮演着非常重要的作用。

7、作死探讨:数组转置

这里我们不用深入了解什么叫做数据转置,其实就是探讨如何实现下面这种效果。

Snipaste_2020-09-01_08-05-25231

说明:我们沿用上面行列互换过后的二维数组,因此在二维数组的表格中执行的顺序是从上到下,一列一列的进行输出,因此上面这张图说的是:数组转置就是将原本竖直排列的数据转置为横向排列的数据。

Snipaste_2020-09-01_08-05-25965423

其实你会发现,转置后的二维数组原来的二维数组,其实完全不是一个数组了。完全变了一个样。转置后的二维数组其实就是一个新的二维数组。那么我们如何创建一个符合条件的二维数据呢?

那么我们如何创建一个符合条件的二维数据呢?

Snipaste_2020-09-30_19-02-37

我们不妨打印出来看一下

Snipaste_2020-09-30_19-05-56

# 神奇的事情发生了,你会发现直接被转置过来了。

那么我们不妨研究一下为什么这样可以被转置过来。

要知道矩阵转置,是有数学理论基础的,我们首先看下什么是矩阵的转置?

其实转置是把一个矩阵里面的某一个元素,他的行号和列好进行一个互换。

Snipaste_2020-08-26_05-15-3812

看到这个是不是有点迷,看不太懂,这个不用怕,我们直接抽离出里面的关键图进行分析。

Snipaste_2020-08-26_05-15-38

结合我们的代码进行理解的话,那么就是下面这个过程。

Snipaste_2020-10-01_11-42-48

我们不妨在这几个地方都将对应的参数打印出来看一下。

Snipaste_2020-10-01_10-36-30

可以清晰的看出,代码执行的整个过程。

Snipaste_2020-10-01_10-42-51

为了能够进一步的的理解数组转置,我们不妨对数组在稍微修改一下。

Snipaste_2020-10-01_12-42-48

然后,我们再运行一下看看效果。

Snipaste_2020-10-01_12-46-44

通过打印的结果你会发现什么呢?

Snipaste_2020-10-01_12-49-55

我们神奇的发现,我们转置之后,有3行4列,变成了4行3列了。

从代码端上看的话,我们也是可以看出为什么会出现这样的情况的。

Snipaste_2020-10-01_12-54-11

那么如果说我们不想这么复杂去理解转置之后的元素变化的话。其实还可以更加形象的方式

我们可以直接将数组倒转过来,就是我们转置之后的元素变化效果。

Snipaste_2020-10-01_12-49-55

8、双数组的封装

和封装myForEach方法的原理一样,我们可以封装一下双数组。前提在于我们需要对双数组足够的了解,封装的代码如下:

Snipaste_2020-09-11_13-58-42

运行看一下:

Snipaste_2020-09-11_13-59-57