跳至内容
0%

Solidity 中的 mapping 是什么?

Alchemy team headshot

作者 Alchemy

发布于 2022年10月4日2 分钟阅读

Solidity 中的映射(mapping)是以键值对形式存储数据的哈希表,其中键可以是 Ethereum 支持的任意内置数据类型。映射是学习 Solidity 开发时需要理解的基础概念。

本文将介绍映射是什么、映射如何工作、映射与数组的区别,并给出映射的示例,帮助你在 Ethereum 及 Optimism、Arbitrum 等兼容 Solidity 的区块链上开发出更好的智能合约。

什么是哈希表?

哈希表是一种以关联方式存储数据的数据结构。哈希表中的数据以数组形式保存,每个数据值都有自己唯一的索引值。哈希表使用数组作为存储介质,并利用哈希技术建立索引,用于插入或定位元素。

Mappings in Solidity Explained by Doug Crescenzi
来源:Mappings in Solidity Explained by Doug Crescenzi

当所需数据的索引已知时,可以极快地返回该数据。因此,无论数据量多大,哈希表的插入和查找操作都非常快速。

Solidity 中的映射是什么?

映射是 Solidity 中的一种哈希表,以键值对形式存储数据,其中键可以是除引用类型之外的任意内置数据类型,值的数据类型则可以是任意类型。

在 Solidity 和 Ethereum 区块链中,映射最常见的用途是将唯一的 Ethereum 地址关联到对应的值类型。

在其他编程语言中,映射相当于字典(dictionary)。

哈希表与 Solidity 映射有什么区别?

映射的作用类似于哈希表,由键类型与对应的值类型组成键值对,其价值在于可以容纳大量的 KeyTypeValueType 的映射关系。映射没有长度,也没有设置键或值这一概念。映射只适用于作为存储引用类型的状态变量。映射初始化时,会包含所有可能的键,并将其映射到字节表示全为零的值。

Solidity 中定义映射的方式与定义其他变量类型相同:

Solidity 数组与映射有什么区别?

Solidity 数组更适合遍历一组数据(例如使用 for 循环),而映射则更适合在已知键的情况下获取值(即无需遍历数据)。

由于在 Solidity 中遍历数组的成本可能比从映射中获取数据更高,而开发者有时既需要在智能合约中存储值,也需要存储对应的键,因此开发者有时会创建一个键的数组,作为引用,之后再从映射中检索对应数据。

开发者不应让 Solidity 中的数组增长过大,因为遍历一个大数组所耗费的 Solidity gas 费用可能超过交易本身的价值,这使得映射成为更节省 gas 的智能合约实现方式

关于映射,还有以下一些特性:

  • 映射没有长度。
  • 映射也不存在“设置”键或值这一概念。
  • 映射只能用于作为存储引用类型的状态变量。

什么是嵌套映射?

嵌套映射是指从一个映射映射到另一个映射。例如,如果我们有用户名和年龄信息,并希望借助一个特殊的 ID 来存储这些信息,使得其他人只能通过该 ID 获取这些信息,这在 Solidity 中被称为双重映射。

下面是一个嵌套映射的示例:

在这个合约中,我们构建了一个嵌套映射,称为 User。在该映射中,我们关联了两个映射:

  1. 一个用于记录特定用户 id 的相关信息;
  2. 一个用于存储特定用户的姓名和年龄。

下面的代码块是一个简单的 getter 函数,用于返回用户的信息。

如何在 Solidity 中使用映射

以下是在 Solidity 中使用映射的示例。以下代码片段的功能是:

  • 从 address 映射到 uint,并确保映射始终返回一个值
  • 如果该值从未被设置过,则返回默认值。
  • 更新映射地址对应的值
  • 将值重置为默认值。
  • 创建从 address 到另一个映射的嵌套映射
  • 即使嵌套映射未初始化,也能从中获取值

Solidity 映射示例

以下是 Solidity 中映射的三个示例:

  1. ERC20 代币余额
  2. 使用布尔逻辑
  3. 查询 DAO 成员

1. ERC20 用户余额

此代码片段将用户地址映射到其地址对应的 ERC20 余额。

2. Solidity 布尔类型映射示例

此代码片段旨在列出候选人名单,并返回每位候选人获得的票数。此示例在 DAO 中有实际应用场景,成员需要对组织事务进行投票。

3. 是否为成员(DAO)

此示例来自 Dominion DAO 智能合约,其中映射了 raisedProposalsstakeholderVotesvotedOncontributorsstakeholders

此代码示例列出了两个 Solidity 结构体的字段:ProposalStructVotedStruct

Solidity 字符串映射

为了更好地理解,我们在构建映射的过程中尝试添加一些值。在以下示例中,我们将:

  • 创建一个合约
  • 定义一个结构体
  • 声明不同的结构体元素
  • 创建一个映射
  • 向映射中添加值

Solidity 映射常见问题

以下是关于 Solidity 映射的一些常见问题:

  1. Solidity 映射的长度是多少?
  2. Solidity 映射的默认值是什么?
  3. 如何公开查看 Solidity 映射?

Solidity 映射的长度是多少?

映射没有长度。键的数据本身并不保存在映射中,而是使用其 keccak256 哈希值来存储该键所指向的值。因此不存在单独意义上的“键”和“值”这一概念。

Solidity 映射默认值类型是什么?

以下是 Solidity 映射的默认值类型:

  • int/uint - 键类型 = 是;值类型 = 是
  • string - 键类型 = 是;值类型 = 是
  • byte/bytes - 键类型 = 是;值类型 = 是
  • address - 键类型 = 是;值类型 = 是
  • struct - 键类型 = 否;值类型 = 是
  • mapping - 键类型 = 否;值类型 = 是
  • enum - 键类型 = 否;值类型 = 是
  • contract - 键类型 = 否;值类型 = 是
  • fixed-sized array - 键类型 = 是;值类型 = 是
  • dynamic-sized array - 键类型 = 否;值类型 = 是
  • multi-dimensional array - 键类型 = 否;值类型 = 是
  • variable - 键类型 = 否;值类型 = 否

如何公开查看 Solidity 映射?

由于该属性是 public 的,你可以使用 Solidity 编译器生成的 getter 函数来访问它。

Background gradient

构建区块链应用

Alchemy 将最强大的 Web3 开发者产品和工具与资源、社区及专业支持结合在一起。