概述

  • TSet是一种快速容器类,(通常)用于在排序不重要的情况下存储唯一元素。
  • TSet 类似于 TMap 和 TMultiMap,但有一个重要区别:TSet 是通过对元素求值的可覆盖函数,使用数据值本身作为键,而不是将数据值与独立的键相关联。
  • TSet 可以非常快速地添加、查找和删除元素(恒定时间)。默认情况下,TSet 不支持重复的键,但使用模板参数可激活此行为。
  • TSet 也是值类型,支持常规复制、赋值和析构函数操作,以及其元素较强的所有权。TSet 被销毁时,其元素也将被销毁。键类型也必须是值类型。
  • TSet 会直接使用 运算符== 比较元素,使用 GetTypeHash 对其进行散列,然后使用标准的堆分配器

创建

TSet<FString> FruitSet; //尚未分配内存

添加

  • Add

    • 如果尝试添加重复键,会替代了旧的条目
    FruitSet.Add(TEXT("Banana"));
    FruitSet.Add(TEXT("Grapefruit"));
    FruitSet.Add(TEXT("Pineapple"));
    // FruitSet == [ "Banana", "Grapefruit", "Pineapple" ] FruitSet.Add(TEXT("Pear"));
    FruitSet.Add(TEXT("Banana"));
    // FruitSet == [ "Banana", "Grapefruit", "Pineapple", "Pear" ]
    // Note:Only one banana entry.

    此处的元素按插入顺序排列,但不保证这些元素在内存中实际保留此排序。如果是新集合,可能会保留插入排序,但插入和删除的次数越多,新元素不出现在末尾的可能性越大。

  • Emplace 代替 Add,避免插入集合时创建临时文件

    FruitSet.Emplace(TEXT("Orange"));
    // FruitSet == [ "Banana", "Grapefruit", "Pineapple", "Pear", "Orange" ]
  • Append 函数进行合并来插入另一个集合中的所有元素

    • 集合中的重复键将会替代目标集合中相应的键
    TSet<FString> FruitSet2;
    FruitSet2.Emplace(TEXT("Kiwi"));
    FruitSet2.Emplace(TEXT("Melon"));
    FruitSet2.Emplace(TEXT("Mango"));
    FruitSet2.Emplace(TEXT("Orange"));
    FruitSet.Append(FruitSet2);
    // FruitSet == [ "Banana", "Grapefruit", "Pineapple", "Pear", "Orange", "Kiwi", "Melon", "Mango" ]

迭代

  • 范围 - for

    for (auto& Elem :FruitSet)
    {
    FPlatformMisc::LocalPrint( *FString::Printf(TEXT(" \"%s\"\n"), *Elem ));
    }
    // 依次输出 "Banana" "Grapefruit" "Pineapple" "Pear" "Orange" "Kiwi" "Melon" "Mango"
  • 迭代器

    • CreateIterator 返回拥有读写访问权限的迭代器,
    • reateConstIterator 返回拥有只读访问权限的迭代器
    for (auto It = FruitSet.CreateConstIterator(); It; ++It)
    {
    FPlatformMisc::LocalPrint( *FString::Printf(TEXT("(%s)\n"), *It ) );
    }

查询

  • Num 函查询集合中保存的元素数量

    int32 Count = FruitSet.Num(); // Count == 8
  • Contains 函数查询是否包含特定元素

    bool bHasBanana = FruitSet.Contains(TEXT("Banana")); // bHasBanana == true
    bool bHasLemon = FruitSet.Contains(TEXT("Lemon")); // bHasLemon == false
  • FSetElementId 结构体可查找集合中某个键的索引。然后,就可使用该索引与 运算符[] 查找元素。

    • 在非常量集合上调用 operator[] 将返回非常量引用,而在常量集合上调用将返回常量引用。
    FSetElementId BananaIndex = FruitSet.Index(TEXT("Banana"));
    // BananaIndex is a value between 0 and (FruitSet.Num() - 1)
    FPlatformMisc::LocalPrint( *FString::Printf( TEXT(" \"%s\"\n"), *FruitSet[BananaIndex] ));
    // Prints "Banana" FSetElementId LemonIndex = FruitSet.Index(TEXT("Lemon"));
    // LemonIndex is INDEX_NONE (-1)
    FPlatformMisc::LocalPrint( *FString::Printf(TEXT(" \"%s\"\n"), *FruitSet[LemonIndex] ));
    // Assert!
  • Find 返回指向元素数值的指针。

    • 如果映射不包含该键,则返回null。
    • 对常量集合调用Find,返回的指针也将为常量
    FString* PtrBanana = FruitSet.Find(TEXT("Banana")); // *PtrBanana == "Banana"
    FString* PtrLemon = FruitSet.Find(TEXT("Lemon")); // PtrLemon == nullptr
  • Array 函数会返回一个 TArray,其中填充了 TSet 中每个元素的一份副本。

    • 被传递的数组在填入前会被清空,因此元素的生成数量将始终等于集合中的元素数量
    TArray<FString> FruitArray = FruitSet.Array();
    // FruitArray == [ "Banana","Grapefruit","Pineapple","Pear","Orange","Kiwi","Melon","Mango" ] (order may vary)

移除

  • Remove 函数可按索引移除元素,

    • 仅建议在通过元素迭代时使用:Remove函数会返回已删除元素的数量。如果给定的键未包含在集合中,则会返回0。
    • 如果 TSet 支持重复的键,Remove 将移除所有匹配元素。
    • 移除元素将在数据结构中留下空
    FruitSet.Remove(0);
    // FruitSet == [ "Grapefruit","Pineapple","Pear","Orange","Kiwi","Melon","Mango" ] int32 RemovedAmountPineapple = FruitSet.Remove(TEXT("Pineapple"));
    // RemovedAmountPineapple == 1
    // FruitSet == [ "Grapefruit","Pear","Orange","Kiwi","Melon","Mango" ]
    int32 RemovedAmountLemon = FruitSet.Remove(TEXT("Lemon"));
    // RemovedAmountLemon == 0
  • EmptyReset 函数可将集合中的所有元素移除

    • Empty 可采用参数指示集合中保留的slack量,而 Reset 则是尽可能多地留出slack量
    TSet<FString> FruitSetCopy = FruitSet;
    // FruitSetCopy == [ "Grapefruit","Pear","Orange","Kiwi","Melon","Mango" ] FruitSetCopy.Empty();
    // FruitSetCopy == []

排序

TSet 可以排序。排序后,迭代集合会以排序的顺序显示元素,但下次修改集合时,排序可能会发生变化。由于排序不稳定,可能按任何顺序显示集合中支持重复键的等效元素。

  • Sort 函数指定排序顺序的二进制谓词

    FruitSet.Sort([](const FString& A, const FString& B) {
    return A > B; // sort by reverse-alphabetical order
    });
    // FruitSet == [ "Pear", "Orange", "Melon", "Mango", "Kiwi", "Grapefruit" ] (order is temporarily guaranteed) FruitSet.Sort([](const FString& A, const FString& B) {
    return A.Len() < B.Len(); // sort strings by length, shortest to longest
    });
    // FruitSet == [ "Pear", "Kiwi", "Melon", "Mango", "Orange", "Grapefruit" ] (order is temporarily guaranteed)

运算符

TSet<FString> NewSet = FruitSet;
NewSet.Add(TEXT("Apple"));
NewSet.Remove(TEXT("Pear"));
// FruitSet == [ "Pear", "Kiwi", "Melon", "Mango", "Orange", "Grapefruit" ]
// NewSet == [ "Kiwi", "Melon", "Mango", "Orange", "Grapefruit", "Apple" ]

Slack

  • Reset 在不取消任何内存的情况下移除集合中的所有元素,从而产生slack

    FruitSet.Reset();
    // FruitSet == [ <invalid>, <invalid>, <invalid>, <invalid>, <invalid>, <invalid> ]
  • Reserve 函数可直接创建slack,例如在添加元素之前预分配内存

    • 预先分配slack会导致以倒序添加新元素。
    FruitSet.Reserve(10);
    for (int32 i = 0; i < 10; ++i)
    {
    FruitSet.Add(FString::Printf(TEXT("Fruit%d"), i));
    }
    // FruitSet == [ "Fruit9", "Fruit8", "Fruit7" ..."Fruit2", "Fruit1", "Fruit0" ]
  • 使用 CollapseShrink 函数可移除 TSet 中的全部slack。

    • Shrink 将从容器的末端移除所有slack,但这会在中间或开始处留下空白元素
    // Remove every other element from the set.
    for (int32 i = 0; i < 10; i += 2)
    {
    FruitSet.Remove(FSetElementId::FromInteger(i));
    }
    // FruitSet == ["Fruit8", <invalid>, "Fruit6", <invalid>, "Fruit4", <invalid>, "Fruit2", <invalid>, "Fruit0", <invalid> ] FruitSet.Shrink();
    // FruitSet == ["Fruit8", <invalid>, "Fruit6", <invalid>, "Fruit4", <invalid>, "Fruit2", <invalid>, "Fruit0" ]
  • CompactCompactStable 函数,将空白空间组合在一起,为调用 Shrink 做好准备

    FruitSet.CompactStable();
    // FruitSet == ["Fruit8", "Fruit6", "Fruit4", "Fruit2", "Fruit0", <invalid>, <invalid>, <invalid>, <invalid> ]
    FruitSet.Shrink();
    // FruitSet == ["Fruit8", "Fruit6", "Fruit4", "Fruit2", "Fruit0" ]

DefaultKeyFuncs

只要类型具有 运算符== 和非成员 GetTypeHash 重载,就可为TSet所用,因为此类型既是元素又是键。然而,不便于重载这些函数时可将类型作为键使用。在这些情况下,可对 DefaultKeyFuncs 进行自定义。为键类型创建 KeyFuncs,必须定义两个typedef和三个静态函数,如下所示:

  • KeyInitType —— 用于传递键的类型。通常抽取自ElementType模板参数。
  • ElementInitType —— 用于传递元素的类型。同样通常抽取自ElementType模板参数,因此与KeyInitType相同。
  • KeyInitType GetSetKey(ElementInitType Element)——返回元素的键。在集合中,通常是元素本身。
  • bool Matches(KeyInitType A, KeyInitType B) —— 如果 A 和 B 等值将返回 true,否则返回 false
  • uint32 GetKeyHash(KeyInitType Key) —— 返回 Key 的散列值。

KeyInitType 和 ElementInitType 是键/元素类型普通传递惯例的typedef。它们通常为浅显类型的一个值和非浅显类型的一个常量引用。请注意,集合的元素类型也是键类型,因此 DefaultKeyFuncs 仅使用一种模板参数 ElementType 定义两者。

TSet 假定在 DefaultKeyFuncs 中使用 Matches 进行对比结果为相等的两个项也将在 KeyFuncs 的 GetKeyHash 中返回相同的值。

其他

CountBytes 和 GetAllocatedSize 函数用于估计内部数组的当前内存使用情况。CountBytes 接受 FArchive 参数,而 GetAllocatedSize 则不接受。这些函数常用于统计报告。

Dump 函数接受 FOutputDevice 并写出关于集合内容的实现信息。还有一个名为 DumpHashElements 的函数,可列出来自所有散列条目的所有元素。这些函数常用于调试。

参考

TSet

【UE4 C++ 基础知识】<7> 容器——TSet的更多相关文章

  1. 【UE4 C++ 基础知识】<11>资源的同步加载与异步加载

    同步加载 同步加载会造成进程阻塞. FObjectFinder / FClassFinder 在构造函数加载 ConstructorHelpers::FObjectFinder Constructor ...

  2. 《两地书》--Kubernetes(K8s)基础知识(docker容器技术)

    大家都知道历史上有段佳话叫“司马相如和卓文君”.“皑如山上雪,皎若云间月”.卓文君这么美,却也抵不过多情女儿薄情郎. 司马相如因一首<子虚赋>得汉武帝赏识,飞黄腾达之后便要与卓文君“故来相 ...

  3. Kubernetes(K8s)基础知识(docker容器技术)

    今天谈谈K8s基础知识关键词: 一个目标:容器操作:两地三中心:四层服务发现:五种Pod共享资源:六个CNI常用插件:七层负载均衡:八种隔离维度:九个网络模型原则:十类IP地址:百级产品线:千级物理机 ...

  4. 【UE4 C++ 基础知识】<3> 基本数据类型、字符串处理及转换

    基本数据类型 TCHAR TCHAR就是UE4通过对char和wchar_t的封装 char ANSI编码 wchar_t 宽字符的Unicode编码 使用 TEXT() 宏包裹作为字面值 TCHAR ...

  5. 【UE4 C++ 基础知识】<12> 多线程——FRunnable

    概述 UE4里,提供的多线程的方法: 继承 FRunnable 接口创建单个线程 创建 AsyncTask 调用线程池里面空闲的线程 通过 TaskGraph 系统来异步完成一些自定义任务 支持原生的 ...

  6. 【UE4 C++ 基础知识】<5> 容器——TArray

    概述 TArray 是UE4中最常用的容器类.其速度快.内存消耗小.安全性高. 其设计时未考虑扩展问题,因此建议在实际操作中勿使用 新建(new) 和 删除(delete) 创建或销毁 TArray ...

  7. 【UE4 C++ 基础知识】<6> 容器——TMap

    概述 TMap主要由两个类型定义(一个键类型和一个值类型),以关联对的形式存储在映射中. 将数据存储为键值对(TPair<KeyType, ValueType>),只将键用于存储和获取 映 ...

  8. JAVA基础知识:容器

    JDK所提供的容器都在java.util包里面,下面开始讨论的都是JDK1.4版本的,只讲述基本知识,不涉及泛型 容器API的类图结构如下图所示 Set:元素无顺序且不可重复      List:元素 ...

  9. 【UE4 C++ 基础知识】<8> Delegate 委托

    概念 定义 UE4中的delegate(委托)常用于解耦不同对象之间的关联:委托的触发者不与监听者有直接关联,两者通过委托对象间接地建立联系. 监听者通过将响应函数绑定到委托上,使得委托触发时立即收到 ...

随机推荐

  1. 窗口函数至排序——SQLServer2012可高用

    常用到的窗口函数 工作中要常对数据进行分析,分析前要对原始数据中找到想要的格式,数据原本存储的格式不一定时我们想要的,要在基础上进行一定的处理,下面介绍的几种方式是常用的数据排序的集中方式,包含 排名 ...

  2. Java - 注释、标识符、关键字

    背景 要开始磕 Java 了,虽然以前学过用过,但是差不多忘光光了... 现在直接搬狂神的视频素材,不再自己总结,要学的东西太多了... 注释 单行注释 // 多行注释 /* */ 文档注释 /** ...

  3. WEB安全性测试之拒绝服务攻击

    1,认证 需要登录帐号的角色 2,授权 帐号的角色的操作范围 3,避免未经授权页面直接可以访问 使用绝对url(PS:绝对ur可以通过httpwatch监控每一个请求,获取请求对应的页面),登录后台的 ...

  4. 《Go语言圣经》阅读笔记:第三章基础数据类型

    第三章 基础数据类型 Go语言将数据类型分为四类: 基础类型 数字 整数 浮点数 复数 字符串 布尔 复合类型 数据 结构体 引用类型 指针 切片 字典 函数 通道 接口类型 在此章节中先介绍基础类型 ...

  5. Intel® QAT加速卡之Ring & Ring Bank

    1. QAT的应用模式 Intel 通讯系列芯片对于每种受支持的加速服务(加密,数据压缩),都支持以下应用模式: 内核模式,其中应用程序和加速服务都在内核中运行空间. 用户空间直接访问在用户空间中运行 ...

  6. 针对Autocad 2014 第二次安装不上的问题

    针对Autocad 2014 第二次安装不上的问题 1. 以下为卸载过程,不用管. 2. 卸载完之后,右击"开始",点击"运行",得到下图:   并输入:&qu ...

  7. 基于flex布局的header

    一.如图 二.思路 1.定义header,设置宽为100%,高为60px,设置绝对定位,使其为漂浮层.在header里添加container,宽设置为版心宽度,并且设置flex布局. 2.在conta ...

  8. 解析Prometheus PromQL

    解析PromQL 目前对Prometheus 的promQL 的解析文章比较少,且Prometheus官方也没有提供一个公共的库来对齐进行解析.下面实现对promQL的解析,并实现注入label功能. ...

  9. Go并发编程--正确使用goroutine

    目录 1. 对创建的gorouting负载 1.1 不要创建一个你不知道何时退出的 goroutine 1.1.1 不要帮别人做选择 1.1.2 不要作为一个旁观者 1.1.3 不要创建不知道什么时候 ...

  10. Java日期时间API系列42-----一种高效的中文日期格式化和解析方法

    中文日期(2021年09月11日 和 二〇二一年九月十一日 )在生活中经常用到,2021年09月11日很好处理直接使用模板:yyyy年MM月dd日:二〇二一年九月十一日比较不好处理,需要每个数字进行转 ...