在以太坊智能合约的世界里,mapping(映射)是一种极其重要且常用的数据结构,它就像一个高效的数字字典或关联数组,允许开发者存储和检索键值对(Key-Value Pairs),其中键(Key)和值(Value)都可以是各种数据类型,理解并熟练运用 mapping,是构建复杂、功能丰富的去中心化应用(DApp)的关键一环。

什么是以太坊 Mapping

mapping 是一种键值对存储的抽象,它定义了一种从一种数据类型(键类型)到另一种数据类型(值类型)的映射关系,与数组(Array)不同,mapping 的元素没有固定的顺序,你不能通过索引来访问或遍历它的所有元素,访问 mapping 中的元素是通过其键来直接进行的,这使得其读取和写入操作在时间复杂度上平均为 O(1),效率非常高。

在 Solidity 中,mapping 的基本语法如下:

mapping(KeyType => ValueType) public mappingName;
  • KeyType:可以是任何基本数据类型,如 uint, int, address, bool, bytes32,甚至是另一个 mapping 或一个结构体(struct),但不能是复杂的、动态大小的类型,如数组、字符串或另一个 mapping(除非嵌套在结构体中)。
  • ValueType:可以是任何数据类型,包括基本类型、数组、结构体,甚至是另一个 mapping
  • public:如果你为 mapping 声明为 public,Solidity 会自动为你生成一个 getter 函数,这个函数允许外部合约或通过 Web3.js 等库传入键来获取对应的值。

Mapping 的工作原理与内存管理

理解 mapping 在以太坊区块链上的存储方式至关重要。

  1. 存储位置mapping 类型的变量只能存储在 存储(Storage) 中,不能存在于内存(Memory)或 calldata 中,这意味着它们是永久性地写在区块链上的状态变量,会消耗 gas。
  2. 虚拟存在的概念mapping 本身并不像数组那样实际存储一组连续的值,相反,它更像是一个“虚拟”的映射关系,当你向 mapping 中写入一个键值对时,以太坊会根据键的哈希值计算出该值在存储槽(Storage Slot)中的具体位置,然后将值存储在那里。
    • mapping(uint => uint) public myMapping;,当你设置 myMapping[5] = 10; 时,以太坊会计算 keccak256(abi.encodePacked(uint256(5))) 得到一个哈希值,这个哈希值指向的存储槽的位置就是 10 存放的地方,键 5 本身并不直接存储,而是通过其哈希值来定位值。
  3. 默认值:当你读取一个从未被设置过的键时,mapping 会返回一个默认值,对于值类型,默认值通常是零值,uint 返回 0bool随机配图