什么是 Solidity 中的映射?

  • Alchemy
  • 发布于 2022-10-05 16:41
  • 阅读 25

本文详细介绍了Solidity中的映射数据结构,包括映射的定义、工作原理及其与数组的区别。作者提供了丰富的代码示例,以帮助开发者更好地理解如何在以太坊及Solidity兼容区块链上实现映射,特别是在智能合约开发中的应用。

在 Solidity 中,映射是存储数据的哈希表,数据以键值对的形式存储,其中键可以是 Ethereum 支持的任何内置数据类型。理解映射是学习 Solidity 开发 的一个基本概念。

本文解释了映射是什么、映射如何工作、映射和数组之间的区别,并提供了映射的示例,以便你能够在 Ethereum 及其支持的区块链(如 Optimism 和 Arbitrum)上开发最佳智能合约。

什么是哈希表?

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

Solidity 中的映射解释图来源: Solidity 中的映射解释图

当需要数据的索引已知时,可以非常迅速地返回该数据。因此,哈希表是一种数据结构,在其中插入和搜索操作都非常快速,无论数据量的大小如何。

什么是 Solidity 中的映射?

映射是 Solidity 中的哈希表,存储为键值对,键可以是任何内置数据类型(不包括引用类型),值的数据类型可以是任何类型。

映射通常在 Solidity 和 Ethereum 区块链中用于将唯一的 Ethereum 地址连接到相应的值类型。

在其他编程语言中,映射相当于一个字典。

哈希表和 Solidity 映射之间有什么区别?

映射作为哈希表运行,具有键类型和相应的值类型对,映射的价值在于它们可以容纳大量的 _KeyTypes_ValueTypes。映射没有长度,也没有设置键或值的概念。映射仅适用于作为存储引用类型的状态变量。当映射被初始化时,它们包含所有可能的键,并映射到其字节表示全部为零的值。

映射在 Solidity 中的定义与任何其他变量类型相同:

mapping(address => uint) public userLevel;

Solidity 数组和映射之间有什么区别?

Solidity 数组 在迭代数据组(例如,使用 for 循环)方面更好,而映射在能够根据已知键获取值时更好(即不需要遍历数据)。

由于在 Solidity 中遍历数组可能比从映射获取数据更昂贵,开发者可能希望在智能合约中同时存储值和其键,因此开发者有时会创建一个键的数组,以作为能从映射中检索数据的引用。

开发者不应让 Solidity 中的数组增长过大,因为遍历一个大型数组可能会导致 Solidity Gas费用高于交易值,从而使映射成为更 Gas高效的智能合约实现

以下是一些关于映射的附加特性:

  • 映射没有长度。

  • 映射也不理解设置键或值的概念。

  • 映射只能用于作为存储引用类型的状态变量。

什么是嵌套映射?

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

这是一个嵌套映射的示例:

pragma solidity >=0.7.0 <0.9.0;
contract Nestmap
{
    mapping(uint256=>mapping(string=>uint256)) public User;
    function adduser(uint256 _Id,string memory _name, uint256 _age) public {
        User[_Id][_name] = _age;
    }
}

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

  1. 一个用于记录特定用户的 id 信息

  2. 一个用于存储特定用户的姓名和年龄。

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

function user(uint256 _Id, string memory _name) public view returns(uint256)
{
    return User[_Id][_name];
}

如何在 Solidity 中使用映射

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

  • 从地址到 uint 的映射,确保映射始终返回一个值

  • 如果值从未设置,将返回默认值。

  • 更新映射地址处的值

  • 将值重置为默认值。

  • 从地址创建到另一个映射的嵌套映射

  • 即使嵌套映射未初始化,也能获取值。

pragma solidity ^0.8.13;
contract Mapping {
// 从地址到 uint 的映射
mapping(address => uint) public myMap;

function get(address _addr) public view returns (uint) {
// 映射始终返回一个值。
// 如果值从未设置,将返回默认值。
    return myMap[_addr];
}

function set(address _addr, uint _i) public {
// 更新此地址处的值
    myMap[_addr] = _i;
}

function remove(address _addr) public {
// 将值重置为默认值。
    delete myMap[_addr];
}
}

contract NestedMapping {
// 嵌套映射(从地址到另一个映射)
mapping(address => mapping(uint => bool)) public nested;

function get(address _addr1, uint _i) public view returns (bool) {
// 你可以从嵌套映射中获取值
// 即使它未初始化
    return nested[_addr1][_i];
}

function set(
address _addr1,
    uint _i,
    bool _boo
    ) public {
    nested[_addr1][_i] = _boo;
    }

    function remove(address _addr1, uint _i) public {
    delete nested[_addr1][_i];
    }
}

Solidity 映射示例

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

  1. ERC20 代币余额

  2. 使用布尔逻辑

  3. 查询 DAO 的成员

1. ERC20 用户余额

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

contract ERC20 is Context, IERC20 { 
using SafeMath for uint256;
using Address for address; 
mapping (address => uint256) private _balances;    ...}

2. Solidity 映射布尔示例

此代码片段旨在列出候选人的名字并返回每位候选人获得的投票数。该示例在 DAO 中有用例,其中预期成员对于组织决策进行投票。

contract Voting {
mapping (bytes32 => uint8) public votesReceived;
mapping (bytes32 => bool) public candidateList;

function Voting(bytes32[] candidateNames) {
for(uint i = 0; i < candidateNames.length; i++) {
candidateList[candidateNames[i]] = true;
}
}
function totalVotesFor(bytes32 candidate) constant returns (uint8) {
require(validCandidate(candidate));
return votesReceived[candidate];
}

function voteForCandidate(bytes32 candidate) {
require(validCandidate(candidate) == true);
votesReceived[candidate] += 1;
}

function validCandidate(bytes32 candidate) constant returns (bool) {
return candidateList[candidate];
}
}

3. 是个成员(DAO)

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

mapping(uint256 => ProposalStruct) private raisedProposals;
mapping(address => uint256[]) private stakeholderVotes;
mapping(uint256 => VotedStruct[]) private votedOn;
mapping(address => uint256) private contributors;
mapping(address => uint256) private stakeholders;

此代码示例列出了两个 Solidity 结构体ProposalStructVotedStruct

struct ProposalStruct {
uint256 id;
uint256 amount;
uint256 duration;
uint256 upvotes;
uint256 downvotes;
string title;
string description;
bool passed;
bool paid;
address payable beneficiary;
address proposer;
address executor;
}

struct VotedStruct {
address voter;
uint256 timestamp;
bool choosen;
}

Solidity 字符串的映射

让我们尝试在构建映射时添加一些值,以便更好地理解。在以下示例中,我们:

  • 创建一个合约

  • 定义一个结构

  • 声明不同的结构元素

  • 创建一个映射

  • 向映射中添加值

pragma solidity ^0.4.18;
// 创建合约
contract mapping_example {
//定义结构
struct student {
//声明不同的
// 结构元素
string name;
string subject;
uint8 marks;
}

// 创建映射
mapping (
address => student) result;
address[] public student_result;

// 函数向映射中添加值
function adding_values() public {
var student
 = result[0xDEE7796E89C82C36BAdd1375076f39D69FafE252];

student.name = "John";
student.subject = "Chemistry";
student.marks = 88;
student_result.push(
0xDEE7796E89C82C36BAdd1375076f39D69FafE252) -1;
}
}

Solidity 映射常见问题解答

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

  1. Solidity 映射的长度是什么?

  2. Solidity 映射的默认值是什么?

  3. 如何公开查看 Solidity 映射?

Solidity 映射的长度是什么?

映射没有长度。 键的数据不会保存在映射中,而是使用 keccak256 哈希 来存储键数据所引用的值。没有键和值“单独存在”的概念。

Solidity 映射的默认值是什么?

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

  • int/uint - 键类型 = 是;值类型 = 是

  • string - 键类型 = 是;值类型 = 是

  • byte/bytes - 键类型 = 是;值类型 = 是

  • address - 键类型 = 是;值类型 = 是

  • struct - 键类型 = 否;值类型 = 是

  • mapping - 键类型 = 否;值类型 = 是

  • enum - 键类型 = 否;值类型 = 是

  • contract - 键类型 = 否;值类型 = 是

  • 固定大小数组 - 键类型 = 是;值类型 = 是

  • 动态大小数组 - 键类型 = 否;值类型 = 是

  • 多维数组 - 键类型 = 否;值类型 = 是

  • 变量 - 键类型 = 否;值类型 = 否

如何公开查看 Solidity 映射?

因为 该属性是公共的,你可以使用由 Solidity 编译器 创建的获取器函数来访问它。

const myContract = new web3.eth.Contract(abiJson, contractAddress); // 返回映射键 `0` 的值
const info = await myContract.methods.locks(0).call();
  • 原文链接: alchemy.com/overviews/so...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Alchemy
Alchemy
江湖只有他的大名,没有他的介绍。