Skip to content

集合

集合类型是编程语言中处理数据集合的核心组件。Sui Framework 提供了一系列基于向量的集合类型,既保持了 vector 的灵活性,又增加了功能性和约束条件。本节介绍这些集合类型及其在实际应用中的用法。

向量(Vector)

在 Move 中,vector 是一种动态数组类型,用于存储任意类型的有序集合。虽然之前已经介绍了 vector 类型,但在集合的上下文中需要进一步探讨其在结构体中的应用场景。

示例:在对象中使用向量

以下代码展示了如何在结构体中定义和操作一个 vector 类型字段。

move
module book::collections_vector;
use std::string::String;

/// 代表一本可以出售的图书
public struct Book has key, store {
    id: UID, // 图书的唯一标识
    name: String, // 图书的名称
}

/// 图书商店结构,包含一组书籍
public struct BookStore has key, store {
    id: UID, // 商店的唯一标识
    books: vector<Book>, // 商店中的图书集合
}

在上述例子中,BookStore 使用 vector 存储一组 Book 实例。这种模式在需要按顺序管理多个对象时非常常见。

VecSet:唯一元素集合

VecSet 是一种集合类型,专门用于存储一组唯一元素。与 vector 不同,VecSet 会自动排除重复元素,因此适合用来管理唯一标识符或地址列表。

示例:VecSet 的使用

move
module book::collections_vec_set;
use sui::vec_set::{Self, VecSet};

public struct App has drop {
    subscribers: VecSet<address>, // 存储唯一订阅者地址
}

#[test]
fun vec_set_playground() {
    let mut set = vec_set::empty<u8>(); // 创建空集合
    set.insert(1); // 添加元素 1
    set.insert(2);
    set.insert(3);

    assert!(set.contains(&1)); // 检查是否包含元素 1
    assert!(set.size() == 3); // 集合大小为 3
    assert!(!set.is_empty()); // 集合非空

    set.insert(3); // 尝试插入重复元素 3
    assert!(set.size() == 3); // 尺寸未改变

    set.remove(&2); // 移除元素 2
    assert!(!set.contains(&2)); // 集合中不再包含 2
}

注意事项

  1. 重复插入:如果尝试插入已存在的元素,操作会静默失败,集合的大小不变。
  2. 元素类型:VecSet 的元素类型必须实现 Ord 能力,以支持排序和去重。

VecMap:键值对集合

VecMap 是一种键值映射集合,用于存储键值对。它类似于哈希表,但在实现上基于向量,因此键的顺序与插入顺序一致。

示例:VecMap 的使用

move
module book::collections_vec_map;
use sui::vec_map::{Self, VecMap};
use std::string::String;

public struct Metadata has drop {
    name: String, // 元数据的名称
    attributes: VecMap<String, String>, // 属性的键值映射
}

#[test]
fun vec_map_playground() {
    let mut map = vec_map::empty(); // 创建空映射
    map.insert(b"id".to_string(), b"12345".to_string()); // 添加键值对
    map.insert(b"type".to_string(), b"book".to_string());

    assert!(map.contains(&b"id".to_string())); // 检查键是否存在
    assert!(map.get(&b"type".to_string()).unwrap() == b"book"); // 获取值
    map.remove(&b"id".to_string()); // 移除键值对
}

特性和行为

  1. 键的唯一性:同一个键只能存在一个值。如果插入重复键,新的值会替换旧值。
  2. 键的能力要求:键必须实现 OrdCopy 能力。

局限性和注意事项

大小限制

集合的大小受到 Move 对象大小的限制。如果集合过大,可能会超出对象限制,导致操作失败。

元素类型

集合类型是严格类型化的。不能将错误类型的元素插入集合,否则会触发编译错误。例如,VecSet<u8> 不能存储 String 类型的元素。

比较行为

集合类型(如 VecSet 和 VecMap)无法直接进行逻辑比较,原因是其内部存储顺序不保证一致。

move
let mut set1 = vec_set::empty();
set1.insert(1);
set1.insert(2);

let mut set2 = vec_set::empty();
set2.insert(2);
set2.insert(1);

assert!(set1 == set2); // 错误:比较可能失败,尽管元素相同

建议使用显式检查方法(如 containssize)进行逻辑比较。

总结

  1. 向量:灵活的动态数组,适用于存储任意有序对象集合。
  2. VecSet:保证唯一性的集合类型,适合管理去重列表。
  3. VecMap:键值映射集合,用于存储关联关系。

这些基于向量的集合提供了良好的类型安全性和基本操作能力,但在存储大量数据时受限于 Move 的对象大小限制。在需要更灵活或大规模数据存储的场景下,可以探索其他方法,例如动态字段。