UE_C++ —— Container TSet

目录

一,TSet

二,Creating and Filling a Set

Editing UPROPERTY TSets

三,Iteration

四,Queries

五,Removal

六,Sorting

七,Operators

八,Slack

九,DefaultKeyFuncs

十,Miscellaneous


        类似于 TMap 和 TMultiMap,但 TSet 是使用数据值本身作为键,而不是将数据值与独立的键相关联;TSet 可以非常快速地添加、查找和删除元素(恒定时间);默认情况下,TSet 不支持重复的键,但使用模板参数可激活此行为;

一,TSet

        TSet是一种快速容器类,通常用于在排序不重要的情况下存储唯一元素;在大多数情况下,只需要一种参数——元素类型;但,TSet 可配置各种模板参数来改变其行为,使其更全面;除了可指定从 DefaultKeyFuncs 的派生结构体来提供散列功能,还可允许多个键拥有相同的值;和其它容器类一样,可设置自定义内存分配器来存储数据;

        和 TArray 一样,TSet 是同质容器,即其所有元素均完全为相同类型;TSet 也是值类型,支持常规复制、赋值和析构函数操作,以及其元素较强的所有权;TSet 被销毁时,其元素也将被销毁;键类型也必须是值类型;

        TSet 使用散列hash,即如给出了 KeyFuncs 模板参数,该参数会告知set如何从某个元素确定键,如何比较两个键是否相等,如何对键进行散列,以及是否允许重复键;它们默认只返回对键的引用,使用 operator== 对比相等性,使用非成员函数 GetTypeHash 进行散列;默认,set中不允许有重复的键;如键类型支持这些函数,则可以将其用作集合键,无需提供自定义 KeyFuncs;要写入自定义 KeyFuncs,可扩展 DefaultKeyFuncs 结构体;

        最后,TSet 可通过任选分配器控制内存分配行为;UE4分配器(如 FHeapAllocator 和 TInlineAllocator)不能用作 TSet 的分配器;实际上,TSet 使用set分配器,该分配器可定义set中使用的散列桶数量以及用于存储元素的标准UE分配器;

        与 TArray 不同,内存中 TSet 元素的相对排序既不可靠也不稳定,对这些元素进行迭代很可能会使它们返回的顺序和它们添加的顺序有所不同;这些元素也不太可能在内存中连续排列;set中的后台数据结构是稀疏数组,即在数组中有空位;从set中移除元素时,稀疏数组中会出现空位;添加新的元素,可填补这些空位;但,即便 TSet 不会打乱元素来填补空位,指向元素的指针仍然可能失效,因为如存储器被填满,又添加了新的元素,整个存储可能会重新分配;

二,Creating and Filling a Set

//创建一个空 TSet,用于存储 FString 数据;
TSet<FString> FruitSet;

        TSet 会直接使用 运算符== 比较元素,使用 GetTypeHash 对其进行散列,然后使用标准的堆分配器;当前尚未分配内存;

        填充set的标准方法是使用 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.

        和 TArray 一样,还可使用 Emplace 代替 Add,避免插入集合时创建临时文件;

FruitSet.Emplace(TEXT("Orange"));
// FruitSet == [ "Banana", "Grapefruit", "Pineapple", "Pear", "Orange" ]

        参数直接传递给键类型的构造函数;可避免为该值创建临时 FString;与 TArray 不同的是,只能使用单一参数构造函数;

        也可使用 Append 函数进行合并来插入另一个集合中的所有元素;和使用 Add 或 Emplace 进行单个添加是相同的;源set中的重复键将会替代目标set中相应的键;

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" ]
Editing UPROPERTY TSets

        用 UPROPERTY 宏和一个可编辑的关键词(EditAnywhereEditDefaultsOnly 或 EditInstanceOnly)标记 TSet,则可在编辑器中添加并编辑元素;

UPROPERTY(Category = SetExample, EditAnywhere)
TSet<FString> FruitSet;

三,Iteration

        TSet 的迭代类似于 TArray,可使用C++的范围for;

for (auto& Elem : FruitSet)
{
	FPlatformMisc::LocalPrint(
		*FString::Printf(
			TEXT(" \"%s\"\n"),
			*Elem
		)
	);
}
// Output:
// 	"Banana"
// 	"Grapefruit"
// 	"Pineapple"
// 	"Pear"
// 	"Orange"
// 	"Kiwi"
// 	"Melon"
// 	"Mango"

        也可用 CreateIterator 和 CreateConstIterators 函数来创建迭代器;CreateIterator 返回拥有读写访问权限的迭代器,而 CreateConstIterator 返回拥有只读访问权限的迭代器;无论哪种情况,均可用这些迭代器的 Key 和 Value 来检查元素;

for (auto It = FruitSet.CreateConstIterator(); It; ++It)
{
    FPlatformMisc::LocalPrint(
        *FString::Printf(
            TEXT("(%s)\n"),
            *It
        )
    );
}

四,Queries

        调用 Num 函数可查询set中保存的元素数量;

int32 Count = FruitSet.Num();
// Count == 8

        要确定set是否包含特定元素,可调用 Contains 函数;

bool bHasBanana = FruitSet.Contains(TEXT("Banana"));
bool bHasLemon = FruitSet.Contains(TEXT("Lemon"));
// bHasBanana == true
// bHasLemon == false

        使用 FSetElementId 结构体可查找集合中某个键的索引;然后,就可使用该索引与 operator[] 查找元素;在非常量set上调用 operator[] 将返回非常量引用,而在常量set上调用将返回常量引用;

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!

        如不确定set中是否包含某个键,可使用 Contains 函数和 operator[] 进行检查;但这并非理想的方法,因为同一键需要进行两次查找才能获取成功;使用 Find 函数查找一次即可完成这些行为;如集合中包含该键,Find 将返回指向元素数值的指针;如映射不包含该键,则返回null;对常量set调用Find,返回的指针也将为常量;

FString* PtrBanana = FruitSet.Find(TEXT("Banana"));
FString* PtrLemon = FruitSet.Find(TEXT("Lemon"));
// *PtrBanana == "Banana"
//  PtrLemon == nullptr

        Array 函数会返回一个 TArray,其中填充了 TSet 中每个元素的一份副本;被传递的数组在填入前会被清空,因此元素的生成数量将始终等于set中的元素数量;

TArray<FString> FruitArray = FruitSet.Array();
// FruitArray == [ "Banana","Grapefruit","Pineapple","Pear","Orange","Kiwi","Melon","Mango" ] (order may vary)

五,Removal

        通过 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" ]
FString RemovedAmountLemon = FruitSet.Remove(TEXT("Lemon"));
// RemovedAmountLemon == 0

        使用 Empty 或 Reset 函数可将set中的所有元素移除;Empty 和 Reset 相似,但 Empty 可采用参数指示保留的slack量,而 Reset 则是尽可能多地留出slack量;

TSet<FString> FruitSetCopy = FruitSet;
// FruitSetCopy == [ "Grapefruit","Pear","Orange","Kiwi","Melon","Mango" ]

FruitSetCopy.Empty();
// FruitSetCopy == []

六,Sorting

        TSet 可被排序,排序后,会以排序的顺序显示元素,但下次修改set时,排序可能会发生变化;由于排序不稳定,可能按任何顺序显示重复键;

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)

七,Operators

        和 TArray 一样,TSet 是常规值类型,可通过标准复制构造函数或赋值运算符进行复制;因为set严格拥有其元素,是深拷贝,所以新set将拥有其自身的元素副本;

TSet<int32, 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

        Slack是不包含元素的已分配内存,调用 Reserve 可分配内存,无添加元素;通过非零slack参数调用 Reset 或 Empty 可移除元素,无需将其使用的内存取消分配;Slack优化了将新元素添加到set的过程,因为可以使用预先分配的内存,而不必分配新内存;它在移除元素时也十分实用,因为系统不需要取消内存分配;在清空并希望用相同或更少的元素立即重新填充的set时,此方法尤其有效;

        TSet 不像 TArray 中的 Max 函数那样可检查预分配元素的数量;

//不取消任何内存的情况下移除所有元素,从而产生slack;
FruitSet.Reset();
// FruitSet == [ <invalid>, <invalid>, <invalid>, <invalid>, <invalid>, <invalid> ]

        使用 Reserve 函数可直接创建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" ]

        预先分配slack会导致以倒序添加新元素;与数组不同,set不维护元素排序,不能指望元素排序稳定或可预测;

        使用 Collapse 和 Shrink 函数可移除 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" ]

        以上 Shrink 只删除了一个无效元素,因为末端只有一个空元素;要移除所有slack,首先应调用 Compact 或 CompactStable 函数,将空白空间组合在一起,为调用 Shrink 做好准备;顾名思义,CompactStable 可在合并空元素时保持元素的排序;

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

九,DefaultKeyFuncs

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

typedef

  • KeyInitType —— 用于传递键的类型,通常抽取自ElementType模板参数;
  • ElementInitType —— 用于传递元素的类型,同样通常抽取自ElementType模板参数,因此与KeyInitType相同;

 static function

  • KeyInitType GetSetKey(ElementInitType Element)——返回元素的键,通常是元素本身;
  • bool Matches(KeyInitType A, KeyInitType B) —— 如 A 和 B 等值将返回 true,否则返回 false
  • uint32 GetKeyHash(KeyInitType Key) —— 返回 Key 的散列值;

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

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

        切勿在更改现有元素时改变来自这两个函数中任一个的结果,因为这会使set的内部散列失效;使用 DefaultKeyFuncs 的默认实现时,此规则也适用于 operator== 和 GetKeyHash 的重载;

十,Miscellaneous

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/973694.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Springboot的jak安装与配置教程

目录 Windows系统 macOS系统 Linux系统 Windows系统 下载JDK&#xff1a; 访问Oracle官网或其他JDK提供商网站&#xff0c;下载适合Windows系统的JDK版本。网站地址&#xff1a;Oracle 甲骨文中国 | 云应用和云平台点击进入下滑&#xff0c;点击进入下载根据自己的系统选择&…

Vue2是如何利用Object.defineProperty实现数据的双向绑定?

我们之前说道过Object.defineProperty方法有一关键特性&#xff0c;就是数据劫持&#xff0c;通过get/set 拦截属性的读取和修改操作。Vue主要是通过数据劫持结合发布-订阅模式来实现的&#xff0c;利用Object.defineProperty来劫持各个属性的setter和getter&#xff0c;在数据…

Transformer解析——(四)Decoder

本系列已完结&#xff0c;全部文章地址为&#xff1a; Transformer解析——&#xff08;一&#xff09;概述-CSDN博客 Transformer解析——&#xff08;二&#xff09;Attention注意力机制-CSDN博客 Transformer解析——&#xff08;三&#xff09;Encoder-CSDN博客 Transforme…

Vue前端开发-Vant之Layout组件

在Vant 中&#xff0c;Layout组件用于元素的响应式布局&#xff0c;分别由van-row和van-col两个组件来实现&#xff0c;前者表示行&#xff0c;后者被包裹在van-row组件中&#xff0c;表示列&#xff0c;共有24列栅格组成&#xff0c;在van-col组件中&#xff0c;span属性表示所…

【YOLOv8】损失函数

学习视频&#xff1a; yolov8 | 损失函数 之 5、类别损失_哔哩哔哩_bilibili yolov8 | 损失函数 之 6、定位损失 CIoU DFL_哔哩哔哩_bilibili 2.13、yolov8损失函数_哔哩哔哩_bilibili YOLOv8 的损失函数由类别损失和定位损失构成 类别损失&#xff1a;BCE Loss 定位损失…

Mac系统下使用Docker快速部署MaxKB:打造本地知识库问答系统

随着大语言模型的广泛应用&#xff0c;知识库问答系统逐渐成为提升工作效率和个人学习的有力工具。MaxKB是一款基于LLM&#xff08;Large Language Model&#xff09;大语言模型的知识库问答系统&#xff0c;支持多模型对接、文档上传和自动爬取等功能。本文将详细介绍如何在Ma…

(Arxiv-2025)ImageRAG:用于参考引导图像生成的动态图像检索

ImageRAG&#xff1a;用于参考引导图像生成的动态图像检索 paper是Tel Aviv University发布在Arxiv 2025的工作 paper title:ImageRAG: Dynamic Image Retrieval for Reference-Guided Image Generation Code:链接 图 1&#xff1a;使用参考图像扩展图像生成模型的生成能力。 在…

企业知识管理平台重构数字时代知识体系与智能服务网络

内容概要 现代企业知识管理平台的演进呈现出全生命周期管理与智能服务网络构建的双重特征。通过四库体系&#xff08;知识采集库、加工库、应用库、评估库&#xff09;的协同运作&#xff0c;该系统实现了从知识沉淀、结构化处理到价值释放的完整闭环。其中&#xff0c;知识图…

高级推理的多样化推理与验证

25年2月来自波士顿大学、NotBadMath.AI、谷歌、哥伦比亚大学、MIT、Intuit公司和斯坦福大学的论文“Diverse Inference and Verification for Advanced Reasoning”。 OpenAI o1、o3 和 DeepSeek R1 等推理 LLM 在数学和编码方面取得重大进展&#xff0c;但仍发现 IMO 组合问题…

23.1 WebBrowser控件

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 WebBrowser控件类似于IE浏览器的文档界面&#xff08;事实上IE也是使用的这个控件&#xff09;&#xff0c;它提供了显示网页及支持…

Django-Vue 学习-VUE

主组件中有多个Vue组件 是指在Vue.js框架中&#xff0c;主组件是一个父组件&#xff0c;它包含了多个子组件&#xff08;Vue组件&#xff09;。这种组件嵌套的方式可以用于构建复杂的前端应用程序&#xff0c;通过拆分功能和视图&#xff0c;使代码更加模块化、可复用和易于维…

计算机网络安全之一:网络安全概述

1.1 网络安全的内涵 随着计算机和网络技术的迅猛发展和广泛普及&#xff0c;越来越多的企业将经营的各种业务建立在Internet/Intranet环境中。于是&#xff0c;支持E-mail、文件共享、即时消息传送的消息和协作服务器成为当今商业社会中的极重要的IT基础设施。然而&#xff0…

AI学习指南DeepSeek篇(6)-DeepSeek论文介绍

1. DeepSeek LLM: Scaling Open-Source Language Models with Longtermism 发布时间: 2024 年 1 月 5 日 主要内容: 基于 Transformer 架构,采用分组查询注意力(GQA)优化推理成本。 支持多步学习率调度器,提升训练效率。 在预训练和对齐(监督微调与 DPO)方面进行了创新…

刺客信条 枭雄 画质设置以及【锁帧60帧】的办法

刺客信条 枭雄 锁帧60帧的办法 画质设置帧率锁60帧办法 画质设置 关爱老电脑和GPU&#xff0c;适当设置一下画质 我们设置画面的时候&#xff0c;可以看游戏右上角的显存占用&#xff0c;进而观察自己这样设置&#xff0c;GPU的显存够不够&#xff1a; 环境质量&#xff1a;超…

适用于复杂背景的YOLOv8改进:基于DCN的特征提取能力提升研究

文章目录 1. YOLOv8的性能瓶颈与改进需求1.1 YOLOv8的优势与局限性1.2 可变形卷积&#xff08;DCN&#xff09;的优势 2. DCN在YOLOv8中的应用2.1 DCN的演变与YOLOv8的结合2.2 将DCN嵌入YOLOv8的结构中2.2.1 DCNv1在YOLOv8中的应用2.2.2 DCNv2与DCNv3的优化 2.3 实验与性能对比…

cesium视频投影

先看效果 使用cesium做视频投影效果&#xff0c;而且还要跟随无人机移动而移动&#xff0c;我现在用定时器更新无人机的坐标来实现效果具体代码如下&#xff1a; 1、CesiumVideo3d.js(某个cesium技术群大佬分享的) // import ECEF from "./CoordinateTranslate"; le…

滚珠花键在使用时需注意什么?

滚珠花键是一种直线运动系统&#xff0c;当花键套利用其中的钢球在经过精密磨削的花键轴上直线运动时&#xff0c;可以传递扭矩。在使用滚珠花键时&#xff0c;需要注意以下几个重要的事项&#xff1a; 1、不要擅自拆卸滚珠花键的各部分&#xff0c;因为这样可能会导致异物进入…

AI助力下的PPT革命:DeepSeek 与Kimi的高效创作实践

清华大学出品《DeepSeek&#xff1a;从入门到精通》分享 在忙碌的职场中&#xff0c;制作一份高质量的PPT往往需要投入大量时间和精力&#xff0c;尤其是在临近截止日期时。今天&#xff0c;我们将探索如何借助 AI 工具 —— DeepSeek 和 Kimi —— 让 PPT 制作变得既快捷又高…

PcVue : 点亮马来西亚砂拉越偏远村庄

导读 背景简介 新项目的需求 实施亮点 成果 背景简介 2021年&#xff0c;砂拉越能源公司&#xff08;Sarawak Energy Berhad&#xff09;启动了一项意义非凡的项目-借助太阳能、微型水力发电机等可再生能源&#xff0c;为砂拉越州偏远村庄送去光明与动力。然而&#xff0c…

图论 之 迪斯科特拉算法求解最短路径

文章目录 题目743.网络延迟时间3341.到达最后一个房间的最少时间I 求解最短路径的问题&#xff0c;分为使用BFS和使用迪斯科特拉算法&#xff0c;这两种算法求解的范围是有区别的 BFS适合求解&#xff0c;边的权值都是1的图中的最短路径的问题 图论 之 BFS迪斯科特拉算法适合求…