6.1 映射的声明
映射(mapping)是Solidity中的哈希表,实现通过key值查询对应的Value,例如:通过调用者的地址查询其余额。声明映射的格式为:
mapping(_keyType => _valueType)
。
示例代码:
// 声明映射 (钱包地址的余额)
mapping (address => uint) public balances;
// 声明嵌套映射 (两个地址是否为朋友关系)
mapping (address => mapping (address => bool)) public isFriend;
6.2 映射相关操作
映射的设置、查询、删除操作示例:
// ***************************映射***************************
// 设置
function setBalance(uint _balance) external {
balances[msg.sender] = _balance;
}
// 查询
function getBalance() external view returns (uint) {
return balances[msg.sender];
}
// 删除
function deleteBalance() external {
delete balances[msg.sender]; // 余额设置为默认值:0
}
// ***************************嵌套映射***************************
// 设置
function setFriend() external {
// 设置合约地址为调用者的朋友
isFriend[msg.sender][address(this)] = true;
}
// 查询
function getFriend() external view returns (bool) {
return isFriend[msg.sender][address(this)];
}
// 删除
function deleteFriend() external {
delete isFriend[msg.sender][address(this)];
}
Solidity中所有数据都有默认值,查询不存在的(未赋值的)key对应的value,并不会报错,而会返回value类型的默认值。
例如,在没有调用设置方法(setBalance()、setFriend()
)的情况下,调用查询方法getBalance()
会返回uint类型的默认值0,调用查询方法getFriend()
会返回bool类型的默认值false。
6.3 映射的规则
- 规则一:key值类型只能为solidity中默认的类型,比如uint、address等,不能使用自定义的结构体。value可以为自定义的结构体。
- 规则二:映射存储位置必须为storage,因此可以用于状态变量、函数中的storage变量、library/internal/private函数的参数和返回值,不能用于其他函数的参数或者返回值。
Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
- 规则三:如果映射声明为public,那么solidity会自动给你创建一个getter函数,可以通过Key来查询对应的Value。
6.4 映射的原理
-
原理1:映射不储存任何键(Key)的信息(例如哪些Key被使用),也没有length的信息。
-
原理2:映射使用keccak256(key)当成offset存取value。
-
原理3:因为Ethereum会定义所有未使用的空间为0,所以未赋值(Value)的键(Key)初始值都是各个type的默认值,如uint的默认值是0。
6.5 可遍历映射
根据原理一,映射不储存任何键(Key)的信息(例如哪些Key被使用),也没有length的信息,因此solidity中映射类型本身不可遍历。
可以通过以下方式实现可遍历映射:
- 定义一个映射(_key=>bool映射),记录key是否存在于映射中。
- 定义一个数组(_key值数组),记录所有存在的(被使用的)key。
一个可遍历映射示例:
contract IterableMapping {
mapping (address => uint) public balances;
// 记录某一个地址(key)是否存在于映射中
mapping (address => bool) public inserted;
// 记录所有存在的地址(key)
address[] public keys;
// 设置键值对
function set(address _key, uint _value) external {
// 设置键值对
balances[_key] = _value;
// 判断键值对在设置前是否存在
if (!inserted[_key]) { // false
inserted[_key] = true;
keys.push(_key);
}
}
// 根据索引获取value
function get(uint _i) external view returns (uint) {
require(_i < keys.length, "index out of range");
return balances[keys[_i]];
}
// 余额汇总
function getTotal() external view returns (uint sum) {
for (uint i = 0;i < keys.length;i++) {
sum += balances[keys[i]];
}
}
}
网友评论