百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

数组(下):数据结构中的数组和编程语言中的数组的区别

yuyutoo 2024-10-12 00:45 11 浏览 0 评论

#技术派的书架#

在 2.1 节中,我们提到了数组是存储相同数据类型的一块连续的存储空间。解读一下:数 组中的数据必须是相同类型的,数组中的数据必须是连续存储的。只有这样,数组才能实现根 据下标快速地访问数据。 有些读者可能会发现,在有些编程语言中,“数组”这种数据类型并不一定完全符合上面 的定义。例如,在 JavaScript 中,数组中的数据不一定是连续存储的,也不一定是相同类型的, 甚至,数组还可以是变长的。

var arr = new Array(4, 'hello', new Date());

在大部分关于数据结构和算法的图书中,在提到二维数组或者多维数组中数据的存储方式 的时候,一般会这样介绍:二维数组中的数据以“先按行,再按列”(或者“先按列,再按行”) 的方式依次存储在连续的存储空间中。如果二维数组定义为 a[n][m],那么 a[i][ j] 的寻址公式 如式(2-4)所示(以“先按行,再按列”的方式存储)。

但是,在有些编程语言中,二维数组并不满足上面的定义,寻址公式也并非如此。例如, Java 中的二维数组的第二维可以是不同长度的,如下所示,而且第二维的 3 个数组(arr[0]、 arr[1] 和 arr[2])在内存中也并非连续存储。

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

难道有些关于数据结构和算法的图书里的讲解脱离实践?难道编程语言中的数组没有完全 按照数组的定义来设计?哪个对呢? 实际上,这两种讲法都没错。编程语言中的“数组”并不完全等同于我们在介绍数据结构 和算法的时候提到的“数组”。编程语言在实现自己的“数组”类型的时候,并不是完全遵循 数据结构中“数组”的定义,而是针对编程语言自身的特点,进行了相应的调整。 在不同的编程语言中,数组这种数据类型的实现方式不大相同。本节利用几种比较典型 的编程语言,如 C、C++、Java 和 JavaScript,向读者介绍一下几种比较有代表性的数组实现 方式。

C/C++ 中数组的实现方式

在 C/C++ 中实现的数组完全符合数据结构中的数组的标准定义,利用一块连续的内存空 间存储相同类型的数据。在 C/C++ 中,无论是基本的类型数据,如 int、long 和 char,还是结 构体、对象,在数组中都是连续存储的。我们通过例子来解释一下。

int arr[3];
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;

在上面的代码中,数组 arr 中存储的是 int 类型的数据,对应 的内存存储格式如图 2-6 所示。从图 2-6 中可以看出,数据存储在一 块连续的内存空间中。 在上面的例子中,数组存储的是基本类型数据,下面我们看一 下在利用数组存储对象(在 C 语言中,对象也称结构体(struct))时,数组在内存的存储格式。

struct Dog { 
 char a; 
 char b;
};
struct Dog arr[3];
arr[0].a = '0'; 
arr[0].b = '1';
arr[1].a = '2'; 
arr[1].b = '3';
arr[2].a = '4'; 
arr[2].b = '5';

在上面的代码中,数组 arr 在内存中的存储格式如图 2-7 所示,数据也是存储在连续的 内存空间中的。 上面介绍的是一维数组的存储格式,下面介绍在 C/C++ 语言中多维数组的存储格式。注 意,多维数组与二维数组类似,我们就用二维数组进行讲解。我们看一下下面这段代码。

struct Dog {
 char a;
 char b;
};
struct Dog arr[3][2];

在上面的代码中,struct Dog arr[3][2] 对应的存储格式如图 2-8 所示。从图 2-8 中 可以发现,C/C++ 语言中的二维数组与数据结构中的二维数组是一样的,数据是以“先按行, 再按列”的方式连续存储的。

在上文中,我们分析了C/C++的基本数据类型数组、对象(结构体)数组,以及二维数组, 它们的数据存储格式完全符合数据结构中对数组的定义。

Java 中数组的实现方式

在介绍完 C/C++ 中的数组后,下面看一下 Java 中的数组。Java 中的数组的实现并没有完 全按照数据结构中数组的定义。我们还是分 3 种情况来分析:基本数据类型数组、对象数组和 二维数组(多维数组)。 首先,我们先来看一下基本数据类型数组,也就是说,数组中存储的是 int、long 和 char等基本数据类型的数据。我们还是利用一段代码来进行讲解。

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

在上面的代码中,arr 数组中的数据在内存中的存储格式如图 2-9 所示。注意,new 申请 的空间在堆中,arr 存储在栈中。arr 存储的是数组空间的首地址。 从图 2-9 可以看出,在 Java 中,基本数据类型数组符合数据结构中数组的定义。数组中 的数据是相同类型的,并且存储在连续的内存空间中。 在介绍完基本数据类型数组后,我们再来看一下对象数组,也就是说,数组中存储的不是 int、long 和 char 这些基本类型数据,而是对象。我们还是用一个例子来说明。

public class Person { 
 private String name; 
 public Person(String name) { 
 this.name = name; 
 }
}
Person arr[] = new Person[3];
arr[0] = new Person("Peter");
arr[1] = new Person("Leo");
arr[2] = new Person("Cina");

在上面的代码中,数组 arr 中存储的是 Person 对象。同样,我们还是把数组中的数据 在内存中的存储格式用图表示,如图 2-10 所示。

从图 2-10 中可以看出,在 Java 中,对象数组的存储格式已经与 C/C++ 中的对象数组的存 储格式不一样了。在 Java 中,对象数组中存储的是对象在内存中的地址,而非对象本身。对 象本身在内存中并不是连续存储的,而是散落在各个地方。 在了解了一维数组的存储方式后,我们再来看一下 Java 中的多维数组。在上文提到过, 多维数组与二维数组类似,因此此处还是使用二维数组进行讲解。注意,Java 中的二维数组与 数据结构中的二维数组有很大的区别。在 Java 中,二维数组中的第二维可以是不同长度的。

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

在上面的代码中,arr 是一个二维数组,第一维长度是 3,第二维的长度各不相同:arr[0] 的长度是 1,arr[1] 的长度是 2,arr[2] 的长度是 3。arr 数组在内存中的存储格式如图 2-11 所示。

上面这个二维数组存储的是基本类型的数据,我们再来看一下在二维数组中存储对象时的 数据存储格式。我们还是用一个例子来说明。

Person arr[][] = new Person[3][];
arr[0] = new Person[1];
arr[1] = new Person[2];
arr[2] = new Person[3];
arr[0][0] = new Person("Peter");
arr[1][1] = new Person("Leo");

总结一下,在 Java 中,除基本类型一维数组之外,对象数组、二维数组与数据结构中的 数组的定义有很大区别。

JavaScript 中数组的实现方式

如果 Java 中的数组只是根据语言自身的特点,在数据结构中的数组基础之上做的改造的 话,那么 JavaScript 这种动态脚本语言中的数组完全被改得“面目全非”了。 在本章开头,我们提到过,JavaScript 中的数组可以存储不同类型的数据,数组中的数 据也不一定是连续存储的,并且能支持变长数组。这完全就是与数据结构中的数组的定义相 反的。 实际上,JavaScript 中的数组的底层实现原理已经完全不符合数据结构中的数组的定义了。也就是说,JavaScript 中的数组只不过是名字叫数组而已,与数据结构中的数组几乎没有关系。 接下来,我们就来看一下 JavaScript 中的数组是如何实现的。实际上,JavaScript 中的数 组会根据存储数据的不同,选择不同的实现方式。 如果数组中存储的是相同类型的数据,JavaScript 就真的会用数据结构中的数组来实现。 也就是说,此时会分配一块连续的内存空间来存储数据。 如果数组中存储的是非相同类型的数据,JavaScript 就会用类似哈希表的结构来存储数据。 也就是说,此时数据并不是连续存储在内存中的。这是 JavaScript 中的数组支持存储不同类型 数据的原因。 如果我们向一个存储了相同类型数据的数组中插入一个不同类型的数据,那么 JavaScript 会将底层的存储结构从数组变成哈希表。 实际上,JavaScript 为了“照顾”一些底层应用的开发者,还提供了 ArrayBuffer 这样一种 数据类型。ArrayBuffer 完全符合标准的数据结构中的数组的定义。ArrayBuffer 分配一块连续 的内存空间,仅仅用来存储相同类型的数据。

本文摘自《数据结构与算法之美》

20个经典数据结构与算法,100个真实项目场景案例,300多幅算法手绘图解,一本在手,算法全有,面试大厂不愁!

豆瓣评分9.5,极客时间畅销专栏集结成书,内容更新30%

下一篇

  • 链表(上):如何基于链表实现 LRU 缓存淘汰算法

喜欢请关注+评论+转发哦

相关推荐

Python操作Word文档神器:python-docx库从入门到精通

Python操作Word文档神器:python-docx库从入门到精通动动小手,点击关注...

Python 函数调用从入门到精通:超详细定义解析与实战指南 附案例

一、函数基础:定义与调用的核心逻辑定义:函数是将重复或相关的代码块封装成可复用的单元,通过函数名和参数实现特定功能。它是Python模块化编程的基础,能提高代码复用性和可读性。定义语法:...

等这么长时间Python背记手册终于来了,入门到精通(视频400集)

本文毫无套路!真诚分享!前言:无论是学习任何一门语言,基础知识一定要扎实,基础功非常的重要,找一个有丰富编程经验的老师或者师兄带着你会少走很多弯路,你的进步速度也会快很多,无论我们学习的目的是什么,...

图解Python编程:从入门到精通系列教程(附全套速查表)

引言本系列教程展开讲解Python编程语言,Python是一门开源免费、通用型的脚本编程语言,它上手简单,功能强大,它也是互联网最热门的编程语言之一。Python生态丰富,库(模块)极其丰富,这使...

Python入门教程(非常详细)从零基础入门到精通,看完这一篇就够

本书是Python经典实例解析,采用基于实例的方法编写,每个实例都会解决具体的问题和难题。主要内容有:数字、字符串和元组,语句与语法,函数定义,列表、集、字典,用户输入和输出等内置数据结构,类和对象,...

Python函数全解析:从入门到精通,一文搞定!

1.为什么要用函数?函数的作用:封装代码,提高复用性,减少重复,提高可读性。...

Python中的单例模式:从入门到精通

Python中的单例模式:从入门到精通引言单例模式是一种常用的软件设计模式,它保证了一个类只有一个实例,并提供一个全局访问点。这种模式通常用于那些需要频繁创建和销毁的对象,比如日志对象、线程池、缓存等...

【Python王者归来】手把手教你,Python从入门到精通!

用800个程序实例、5万行代码手把手教你,Python从入门到精通!...

Python从零基础入门到精通:一个月就够了

如果想从零基础到入门,能够全职学习(自学),那么一个月足够了。...

Python 从入门到精通:一个月就够了

要知道,一个月是一段很长的时间。如果每天坚持用6-7小时来做一件事,你会有意想不到的收获。作为初学者,第一个月的月目标应该是这样的:熟悉基本概念(变量,条件,列表,循环,函数)练习超过30个编...

Python零基础到精通,这8个入门技巧让你少走弯路,7天速通编程!

Python学习就像玩积木,从最基础的块开始,一步步搭建出复杂的作品。我记得刚开始学Python时也是一头雾水,走了不少弯路。现在回头看,其实掌握几个核心概念,就能快速入门这门编程语言。来聊聊怎么用最...

神仙级python入门教程(非常详细),从0到精通,从看这篇开始!

python入门虽然简单,很多新手依然卡在基础安装阶段,大部分教程对一些基础内容都是一带而过,好多新手朋友,对一些基础知识常常一知半解,需要在网上查询很久。...

Python类从入门到精通,一篇就够!

一、Python类是什么?大家在生活中应该都见过汽车吧,每一辆真实存在、能在路上跑的汽车,都可以看作是一个“对象”。那这些汽车是怎么生产出来的呢?其实,在生产之前,汽车公司都会先设计一个详细的蓝图...

学习Python从入门到精通:30天足够了,这才是python基础的天花板

当年2w买的全套python教程用不着了,现在送给有缘人,不要钱,一个月教你从入门到精通1、本套视频共487集,本套视频共分4季...

30天Python 入门到精通(3天学会python)

以下是一个为期30天的Python入门到精通学习课程,专为零基础新手设计。课程从基础语法开始,逐步深入到面向对象编程、数据处理,最后实现运行简单的大语言模型(如基于HuggingFace...

取消回复欢迎 发表评论: