UE5 C++ TSet
概念
TSet
是一种快速容器类,用于在排序不重要的情况下存储唯一元素TSet
也是值类型,支持常规复制、赋值和析构函数操作,以及其元素较强的所有权TSet
被销毁时,其元素也将被销毁。键类型也必须是值类型
创建
TSet<FString> FruitSet;
添加元素
-
Add
将提供键加入setFruitSet.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
函数来插入另一个集合中的所有元素Set<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-range
for (auto& Elem :FruitSet) { FPlatformMisc::LocalPrint( *FString::Printf( TEXT(" \"%s\"\n"), *Elem ) ); } // Output: // "Banana" // "Grapefruit" // "Pineapple" // "Pear" // "Orange" // "Kiwi" // "Melon" // "Mango"
- 迭代器
CreateIterator
返回拥有读写访问权限的迭代器CreateConstIterator
返回拥有只读访问权限的迭代器
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[]
查找元素 - 在非常量集合上调用
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
将返回指向元素数值的指针- 如果映射不包含该键,则返回
nullptr
- 对常量集合调用
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" ] FString RemovedAmountLemon = FruitSet.Remove(TEXT("Lemon")); // RemovedAmountLemon == 0
Empty
或Reset
函数可将集合中的所有元素移除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)
运算符
- 和
TArray
一样,TSet
是常规值类型,可通过标准复制构造函数或赋值运算符进行复制 - 因为集合严格拥有其元素,复制集合的操作是深层的,所以新集合将拥有其自身的元素副本
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
- Slack是不包含元素的已分配内存
- 调用
Reserve
可分配内存,无需添加元素 - 通过非零slack参数调用
Reset
或Empty
可移除元素,无需将其使用的内存取消分配 - Slack优化了将新元素添加到集合的过程,因为可以使用预先分配的内存,而不必分配新内存
- 它在移除元素时也十分实用,因为系统不需要将内存取消分配
- 在清空希望用相同或更少的元素立即重新填充的集合时,此方法尤其有效
FruitSet.Reset(); // FruitSet == [ <invalid>, <invalid>, <invalid>, <invalid>, <invalid>, <invalid> ]
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" ]
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" ]