关于 Mappings

Solidity 中 Mappings 的概念类似于 java 中的 hashmap 或者 python 中的 dictionnary。它们使用都起来有一点像 hash 表,

尽管它们在以下方面略有不同:

在 solidity 中所有可能的变量都以默认值初始化。

正因为如此, mappings 是没有长度的。没有设置一个 key 或者 value 的概念。关键数据不是储存在一个 mapping 中的,相反的它的 keccak256 hash 值用来存储关键数据指向的 value 值。

如何来定义一个 Mapping?

mapping (_KeyType => _ValueType) mappingName;

一个好建议:在 mapping 变量名之前使用 public 关键字。这将会自动为 mapping 创建一个 getter 方法。你只需要通过传入**_KeyType参数给 getter 就能返回_ValueType.**

mapping (_KeyType => _ValueType) public mappingName;

如何使用一个 mapping?

  • 对关联关系非常有用,就像将一个 eth 的地址和一个 uint 的整数关联起来 mapping(address => _valueType) my_mapping
  • mapping(uint => _valueType) my_mapping

举个例子在游戏中我们把玩家的地址和玩家的等级关联起来

mapping(address => uint) public userLevel;

另一个例子列出地址是否能够发送 eth?

mapping(address => bool) allowedToSend;

另外一个例子 mapping 的 value 值是一个 struct 类型。

struct someStruct {}
mapping(uint => someStruct) canDoSomething;
uint canDoSomethingKey = 0;
function addCanDoSomething() {
canDoSomething[canDoSomethingKey] = someStruct(arg1, arg2, ...);
canDoSomethingKey++;
}

如何从 Mapping 中获得 value 值?

function currentLevel(address userAddress) public view returns (uint) {
return userLevel[userAddress];
}

使用 Mapping 作为另一个 Mapping 的 value 值

contract C {
struct S { uint a; uint b; }
uint x;
mapping(uint => mapping(uint => S)) data;
}

循环一个 mapping?

因为开头所说的原因不能直接去循环一个 mapping 变量。

mappings are virtually initialised such that every possible key exists and is mapped to a value

然而可以实现一个基于 mapping 之上的数据结构,来使得 mapping 可以被循环。

可以记录一个 counter 的计数器,来告诉你 mapping 的长度当有新增的 value 值的时候。

Mappings 作为函数的参数

这是一个比较难得主题,但是我们还是尝试去探讨一下。Mappings 可以作为参数传到函数中,但是它们必须满足下面的 2 个要求:

  1. Mappings 只能作为内部和私有函数的参数
  2. Mappings 作为参数传递时,数据的存储位置只能是 storage

你不能使用 Mappings 的地方

  • 不能直接循环 mapping 类型
  • Mappings 在 solidity 中参数不能作为参数传递到公有函数和外部函数
  • Mappings 不能作为函数的返回值
Variable TypeKey TypeValue Type
int / uint✔️✔️
string✔️✔️
byte / bytes✔️✔️
address✔️✔️
struct✔️
mapping✔️
enum✔️
contract✔️
* fixed-sized array T[k]✔️✔️
* dynamic-sized array T[]✔️
* multi-dimentional array T[][]✔️
variable