当前位置: 首页 > news >正文

学习虚幻C++开发日志——TSet

TSet

官方文档:虚幻引擎中的Set容器 | 虚幻引擎 5.5 文档 | Epic Developer Community (epicgames.com)

TSet 是通过对元素求值的可覆盖函数,使用数据值本身作为键,而不是将数据值与独立的键相关联。

默认情况下,TSet 不支持重复的键,但使用模板参数可激活此行为。

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

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

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

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

与 TArray 不同的是,内存中 TSet 元素的相对排序既不可靠也不稳定,对这些元素进行迭代很可能会使它们返回的顺序和它们添加的顺序有所不同。这些元素也不太可能在内存中连续排列。集合中的后台数据结构是稀疏数组(在数组中有空位。从集合中移除元素时,稀疏数组中会出现空位)。将新的元素添加到阵列可填补这些空位。

但是,即便 TSet 不会打乱元素来填补空位,指向集元素的指针仍然可能失效,因为如果存储器被填满,又添加了新的元素,整个存储可能会重新分配。

1.创建和填充集合

创建一个MapActor类并继承于Actor,其与TArray创建方法一样就不一一详细介绍;

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void InitSet();

源文件增添代码:

void ASetActor::InitSet()
{TSet<FString> FruitSet;//此处的元素按插入顺序排列,但不保证这些元素在内存中实际保留此排序。//如果是新集合,可能会保留插入排序,但插入和删除的次数越多,新元素不出现在末尾的可能性越大。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.//此处,参数直接传递给键类型的构造函数。这可以避免为该值创建临时 FString。//与 TArray 不同的是,只能使用单一参数构造函数将元素放到集合中。FruitSet.Emplace(TEXT("Orange"));//追加元素,用Emplace函数代替Add避免插入集合时创造临时文件// FruitSet == [ "Banana", "Grapefruit", "Pineapple", "Pear", "Orange" ]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" ]
}

注意元素以及Num的变化,从而去理解TSet键的原理 (使用数据值本身作为键)

2.编辑UPROPERTY TSet

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

Actor类头文件增添代码:

public:UPROPERTY(BlueprintReadWrite,EditAnyWhere,Category = SetExample)TSet<FString> MyFruitSet;

在蓝图可编辑操作

此处为FString,但其也可以放结构体

3.迭代

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void LoopSet();

源文件增添代码:

void ASetActor::LoopSet()
{TSet<FString> FruitSet;FruitSet.Add(TEXT("Banana"));FruitSet.Add(TEXT("Grapefruit"));FruitSet.Add(TEXT("Pineapple"));//依次输出元素for (auto& Elem :FruitSet)//此处的auto可换为FString,其本质时自动推导类型{FPlatformMisc::LocalPrint(*FString::Printf(TEXT(" \"%s\"\n"),*Elem));}//创建迭代器(如为CreateConstIterators 函数则为常量迭代器)for (auto It = FruitSet.CreateIterator(); It; ++It){//注意后面*(*It),第一个*是把FString类型变成TCHAR,第二个是指针,因为迭代器It是地址FPlatformMisc::LocalPrint(*FString::Printf(TEXT("(%s)\n"),*(*It)));}for (auto It = FruitSet.CreateConstIterator(); It; ++It){FPlatformMisc::LocalPrint(*FString::Printf(TEXT("(%s)\n"),*(*It)));}
}

4.查询

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void QuerySet();

源文件增添代码:

void ASetActor::QuerySet()
{TSet<FString> FruitSet;bool bHave=false;FruitSet.Add(TEXT("Banana"),&bHave);//此处bHave为falseFruitSet.Add(TEXT("Grapefruit"));FruitSet.Add(TEXT("Pineapple"));FruitSet.Add(TEXT("Banana"),&bHave);//此处使得bHave为trueint32 Count = FruitSet.Num();// Count == 3//要确定集合是否包含特定元素,可按如下所示调用 Contains 函数bool bHasBanana = FruitSet.Contains(TEXT("Banana"));bool bHasLemon = FruitSet.Contains(TEXT("Lemon"));// bHasBanana == true// bHasLemon == false//使用 FSetElementId 结构体可查找集合中某个键的索引。FSetElementId SetElementId=Fruit.Add(TEXT("Water"));//FSetElementId为标识符,此处SetElementId={Index=3}FruitSet[SetElementId]+=TEXT("Modify");//此处将Index为3的元素进行修改为"WaterModify"//使用 Find 函数查找一次即可完成这些行为。//如果集合中包含该键,Find 将返回指向元素数值的指针。如果映射不包含该键,则返回null。对常量集合调用Find,返回的指针也将为常量。FString* PtrBanana = FruitSet.Find(TEXT("Banana"));FString* PtrLemon = FruitSet.Find(TEXT("Lemon"));// *PtrBanana == "Banana"//  PtrLemon == nullptr//Array 函数会返回一个 TArray,其中填充(覆盖,即使有元素在内)了 TSet 中每个元素的一份副本。因此元素的生成数量将始终等于集合中的元素数量TArray<FString> FruitArray = FruitSet.Array();//用Array函数转换为TArray数组
}

 5.移除

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void RemoveSet();

源文件增添代码:

void ASetActor::RemoveSet()
{TSet<FString> FruitSet;FruitSet.Reserve(4);FruitSet.Add(TEXT("Banana"));FruitSet.Add(TEXT("Grapefruit"));FruitSet.Add(TEXT("Pineapple"));FruitSet.Add(TEXT("GG"));FruitSet.Add(TEXT("HH"));FruitSet.Add(TEXT("JJ"));//Remove函数有两种使用参数方法FruitSet.Remove(FSetElementId::FromInterger(0));//通过索引移除第一个元素//Remove函数会返回已删除元素的数量。int32 GGNum=FruitSet.remove(TEXT("GG"));//GGNum=1int32 MMNum=FruitSet.remove(TEXT("MM"));//MMNum=0TSet<FString> FruitSet1=FruitSet;FruitSet1.Reset();//将集合中的所有元素移除,但内存空间还在TSet<FString> FruitSet2=FruitSet;FruitSet2.Empty(0);//此操作是将元素及其内存空间都删除
}

6.排序&运算符&Slack

6.1排序

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

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void SortSet();

源文件增添代码:

void ASetActor::SortSet()
{TSet<FString>FruitSet={ "Orange","Pear", "Melon", "Grapefruit", "Mango", "Kiwi"};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)//Sort 函数使用指定排序顺序的二进制谓词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)
}

6.2运算符

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

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void OpeatorSet();

源文件增添代码:

void ASetActor::SortSet()
{TSet<FString>FruitSet={ "Orange","Pear", "Melon", "Grapefruit", "Mango", "Kiwi"};TSet<FString> NewSet = FruitSet;//在新的进行增删改查NewSet.Add(TEXT("Apple"));NewSet.Remove(TEXT("Pear"));
}
//在移除目录处也有提及

6.3Slack 

其用法与TMap容器大致相似

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void SlackSet();

源文件增添代码:

void ASetActor::SortSet()
{TSet<FString>FruitSet={ "Orange","Pear", "Melon", "Grapefruit", "Mango", "Kiwi"};//Reset函数可在不取消任何内存的情况下移除集合中的所有元素,从而产生slackFruitSet.Reset();FruitSet.Reserve(10);//在原基础上追加预分配10个内存for (int32 i = 0; i < 10; ++i){FruitSet.Add(FString::Printf(TEXT("Fruit%d"), i));}
// FruitSet == [ "Fruit9", "Fruit8", "Fruit7" ..."Fruit2", "Fruit1", "Fruit0" ]// 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> ]//Shrink裁剪元素FruitSet.Shrink();//注意此处数组Max值为10 
// FruitSet == ["Fruit8", <invalid>, "Fruit6", <invalid>, "Fruit4", <invalid>, "Fruit2", <invalid>, "Fruit0" ]//CompactStable函数压缩元素,Compact函数则可能改变排序FruitSet.CompactStable();// FruitSet == ["Fruit8", "Fruit6", "Fruit4", "Fruit2", "Fruit0", <invalid>, <invalid>, <invalid>, <invalid> ]FruitSet.Shrink();// FruitSet == ["Fruit8", "Fruit6", "Fruit4", "Fruit2", "Fruit0" ]
}

 


http://www.mrgr.cn/news/54232.html

相关文章:

  • 《中国结算全国股份转让系统—结算参与人数据接口规范》
  • Python+Flask接口判断身份证省份、生日、性别、有效性验证+docker部署+Nginx代理运行
  • WebSocket状态码及异常报错1006
  • TCP/IP 协议【四次挥手】简要说明
  • 【ERROR】ubuntu source: not found
  • 网络安全基础知识点_网络安全知识基础知识篇
  • 推荐系统 # 二、推荐系统召回:协同过滤 ItemCF/UserCF、离散特征处理、双塔模型、自监督学习、多路召回、曝光过滤
  • MySQL 索引:优化数据库性能的关键
  • Java的重载和主要内存区
  • 开发工具(上)
  • [SAP ABAP] SE11定义数据类型(结构与表类型)
  • 模型轻量化1--模型剪枝
  • AI周报(10.13-10.19)
  • 把自己写的文章发布在各大媒体网站上难不难?
  • 【每日一题】【算法双周赛】【第 20 场 小白入门赛评价/分享】赛后另类AI写题分析分享
  • 2025年天津仁爱学院专升本动画化学工程与工艺专业对应专业限制
  • 《嵌入式最全面试题-Offer直通车》目录
  • Lua字符串
  • JDK 1.6主要特性
  • 我的JAVA项目构建
  • 怎么修改编辑PDF的内容,有这4个工具就行了。
  • MySQL-20.多表设计-一对一多对多
  • 解锁A/B测试:如何用数据驱动的实验提升你的网站和应用
  • 速盾:为什么高防cdn比普通cdn效果更好?
  • 利士策分享,财富与福报,有没有内在联系?
  • 【Macbook air 2017 升级换硬盘遇到的问题】