当前位置: 首页 > 工具软件 > Nice UE > 使用案例 >

UE C++基础 | 常用数据容器 | TArray、TMap、TSet

皇甫逸清
2023-12-01

UE C++基础 | 常用数据容器 | TArray、TMap、TSet

  • Unreal最常用的三种数据容器为TArray、TMap、TSet
  • 因为这三种容器是支持引擎反射的。(在蓝图中使用)

TArray

  • TArray 是虚幻C++中的动态数组
  • TArray 特点: 速度快,内存消耗小,安全性高。
  • TArray 所有元素均完全为相同类型,不能进行不同元素类型的混合。

初始化

TArray<int32> IntArray; // 创建一个空数组

// 使用 Init 初始化数组 
IntArray.Init(8, 5);    // [8,8,8,8,8]

// 使用 Add 或 Emplace 添加数组元素
// Emplace 可以避免创建不必要的临时变量,效率比 Add 高。
IntArray.Add(5);        // ==> [8,8,8,8,8,5]
IntArray.Emplace(3);    // ==> [8,8,8,8,8,5,3]

TArray<int32> IntArrayB;
IntArrayB.Init(3, 2);   // ==> [3,3]
int32 IntArrayC[] = {6, 6};

// 使用 Append 可以将其它的 TArray 或 常规数组 一次性添加在后面。
IntArray.Append(IntArrayB); // ==> [8,8,8,8,8,5,3,3,3]
IntArray.Append(IntArrayC, ARRAY_COUNT(IntArrayC)); // ==> [8,8,8,8,8,5,3,3,3,6,6]

// 使用 AddUnique 添加一个TArray中不存在的元素,如果已经存在则不会添加。
IntArray.AddUnique(1); // ==> [8,8,8,8,8,5,3,3,3,6,6,1]
IntArray.AddUnique(1); // ==> 无法添加

// 使用 Insert 在指定索引处插入元素。
IntArray.Insert(4, 1); // ==> [8,4,8,8,8,8,5,3,3,3,6,6,1]

// 使用 SetNum 设置 TArray 元素数量,如果大于当前数量,则使用元素类型的默认构造函数创建新元素,如果数量小于当前数量,则将移除多余的元素。
// 通常在初始化后分配一个数量。
IntArray.SetNum(5); // ==> [8,4,8,8,8]
IntArray.SetNum(8); // ==> [8,4,8,8,8,0,0,0]

// IntArray == [3,3,6,2,1,3,8,3,7,5]
// 使用 RemoveSingle 只会移除TArray中与传入元素相等的最靠前的一个元素。
IntArray.RemoveSingle(3) // ==> [3,6,2,1,3,8,3]
// 使用 Remove 会移除与传入元素相等的所有元素,
IntArray.Remove(3) // ==> [6,2,1,8,7,5]
// 使用 RemoveAt 按元素索引移除元素,索引必须存在,否则会出现运行时错误。
IntArray.RemoveAt(2) // ==> [6,2,8,7,5]
// 使用 RemoveAll 以 Lambda 表达式的方式来移除具有某些特征的所有元素。
IntArray.RemoveAll([](const int32& Num){Num % 2 == 0}) // ==> [7,5]
// 使用 Pop 移除最后一个元素。
IntArray.Pop(); // ==> [7]
// 使用 Empty 移除数组中所有元素。
IntArray.Empty(); // ==> []
// 使用 Reset 移除数组中所有元素,但不释放内存。
IntArray.Reset(); // ==> []

// 当元素被移除时,其后的元素会向前移,移动过程存在开销。
// 可使用 RemoveSwap、RemoveAtSwap 和 RemoveAllSwap 来减少开销, 通过牺牲剩余元素的排序将不被保证。

  • 排序
// StringArray == ["!", "Food", "Good", "Hi", "Animal", "Nice"]

// 使用[]索引直接修改值
IntArray[0] = TEXT("?") // ==> ["?", "Food", "Good", "Hi", "Animal", "Nice"]

// 使用 Sort 进行排序
IntArray.Sort()	// ==> ["?", "Animal", "Food", "Good", "Hi", "Nice"]

// 使用 Sort 提供 lambda 谓词进行排序
IntArray.Sort([](const FString& A, const FString& B){
	return A.Len() < B.Len();
	}) // ==> ["?", "Hi", "Good", "Food", "Nice", "Animal"]

// 使用 HeapSort 提供堆排序
IntArray.HeapSort([](const FString& A, const FString& B){
	return A.Len() < B.Len();
	}) // ==> ["?", "Hi", "Good", "Food", "Nice", "Animal"]

// 使用 StableSort 提供稳定排序,排序后保证等值元素的相对排序。
// 基于 ["!", "Food", "Good", "Hi", "Animal", "Nice"] 排序
IntArray.StableSort([](const FString& A, const FString& B){
	return A.Len() < B.Len();
	}) // ==> ["?", "Hi", "Food", "Good", "Nice", "Animal"]

  • 容器操作
// IntArray == [3,6,2,1,8]

// 使用 Num 得到数组的元素数量
int32 Nums = IntArray.Num(); // 目前存了5个元素 ==> Nums == 5

// 使用 GetSlack 找出数组中的Slack量,相当于Max()-Num()
int32 Max = IntArray.Max();			// 目前总共能存22个元素 ==> Max == 22
int32 Slack = IntArray.GetSlack(); 	// 目前还能存17个元素 ==> Slack == 17

// 使用 Shrink 移除所有Slack。
IntArray.Shrink();
int32 Nums = IntArray.Num();		// ==> Nums == 5
int32 Slack = IntArray.GetSlack();	// ==> Slack == 5

// 使用 GetTypeSize 获取单个元素的大小
uint32 ElementSize = IntArray.GetTypeSize(); // ==> ElementSize == sizeof(uint32)

// 使用 [] 输入元素索引得到元素值
int32 Value = IntArray[2];    // ==> Value == 2

// 使用 Last 反向索引 
// StrArr.Top() == StrArr.Last() == StrArr.Last(0)
int32 ElemEndA = StrArr.Last(); // ==> ElemEndA == 8
int32 ElemEndB = StrArr.Last(0);// ==> ElemEndA == 8
int32 ElemEndC = StrArr.Last(1);// ==> ElemEndA == 1

// 使用 Top 得到数组最后一个元素
int32 ElemTop = StrArr.Top();   // ==> ElemEndA == 8

// 当索引小于零或大于等于Num()时,为无效索引,会引起运行时错误。可以使用 IsValidIndex 函数来确定索引是否有效。
bool bIsValid = IntArray.IsValidIndex(-1);  // ==> bIsValid == false
bool bIsValid = IntArray.IsValidIndex(2);   // ==> bIsValid == true
bool bIsValid = IntArray.IsValidIndex(6);   // ==> bIsValid == false

// 判断TArray是否包含某个特定元素,使用 Contains:
bool bIsContain = IntArray.Contains(1);     // ==> bIsValid == true
bool bIsContain = IntArray.Contains(4);     // ==> bIsValid == false
// 使用 ContainsByPredicate 可以用 Lambda表达式 来判断是否包含具有某些特征的元素。
bool bIsContainByPredicate = IntArray.ContainsByPredicate([](const int32& Num){Num % 2 == 0})   // 存在偶数 ==> true

// 使用 Find 查找某个元素是否存在并返回其索引,FindLast从后往前查找。
int32 Index = IntArray.Find(1);       // ==> Index == 3
int32 Index = IntArray.FindLast(1);   // ==> Index == 1
int32 Index = IntArray.Find(5);   // ==> Index == INDEX_NONE

// 使用 IndexOfByKey 与 Find 类似。
int32 Index = IntArray.IndexOfByKey(1);   // ==> Index == 3
int32 Index = IntArray.IndexOfByKey(5);   // ==> Index == INDEX_NONE

// 使用 IndexOfByPredicate 可以使用 Lambda表达式 来查找具有某些特征的元素。
int32 Index = IntArray.IndexOfByPredicate([](const int32& Num){Num % 2 == 0})   // 第一个偶数的索引 ==> index == 1 

// 使用 FindByKey 可以返回找到的元素的指针
auto* Ptr = IntArray.FindByKey(1); // ==> Ptr == &IntArray[3]

// 使用 FindByPredicate 可以使用 Lambda表达式 来查找具有某些特征的元素的指针。
auto* Ptr = IntArray.FindByPredicate([](const int32& Num){Num % 2 == 0})   // ==> Ptr == &IntArray[1]

// 使用 FilterByPredicate 将会返回一个具有某个特征的元素数组。
auto Filter = IntArray.FilterByPredicate([](const int32& Num){Num % 2 == 0}) // ==> Filter == [6, 2, 8]
  • Find 和 IndexOfByKey 源码比较如下。
// Find是将数组元素转换成你传入的类型在和你传入的数据进行比较。
// IndexOfByKey是直接将数组元素与你传入的数据进行比较。

// Find 函数源码 :
SizeType Find(const ElementType& Item) const
{
const ElementType* RESTRICT Start = GetData();
for (const ElementType* RESTRICT Data = Start, *RESTRICT DataEnd = Data + ArrayNum; Data != DataEnd; ++Data)
{
	if (*Data == Item)
	{
		return static_cast<SizeType>(Data - Start);
	}
}
return INDEX_NONE;
}

// IndexOfByKey 函数源码 :
template <typename KeyType>
SizeType IndexOfByKey(const KeyType& Key) const
{
const ElementType* RESTRICT Start = GetData();
for (const ElementType* RESTRICT Data = Start, *RESTRICT DataEnd = Start + ArrayNum; Data != DataEnd; ++Data)
{
	if (*Data == Key)
	{
		return static_cast<SizeType>(Data - Start);
	}
}
return INDEX_NONE;
}
  • 遍历操作。(注意:遍历时不要做删除操作)
// 基于索引的for循环遍历
for (int32 Index = 0; Index != IntArray.Num(); ++Index)
{
	IntArray[Index]; // 遍历的值
}
// auto遍历
for (auto& Int : IntArray)
{
	Int;	// 遍历的值
}
// 迭代器遍历
// CreateIterator 返回拥有读写访问权限的迭代器,
// CreateConstIterator 返回拥有只读访问权限的迭代器
for (auto It = IntArray.CreateConstIterator(); It; ++It)
{
	*It;	// 遍历的值
}

  • TArray 拥有支持二叉堆数据结构的函数。
// IntArray == [1,2,3,4,5,6,7,8,9,10]

// 使用 Heapify 将现有数组转换为堆
IntArray.Heapify(); 	// ==> IntArray == [1,2,4,3,6,5,8,10,7,9]

// 使用 HeapPush 将新元素添加到堆,对其他节点进行重新排序。
IntArray.HeapPush(4); 	// ==> IntArray == [1,2,4,3,4,5,8,10,7,9,6]

// 使用 HeapPop 移除堆顶结点。
int32 TopNode;
IntArray.HeapPop(TopNode); // ==> TopNode == 1; IntArray == [2,3,4,6,4,5,8,10,7,9]

// 使用 HeapRemoveAt 删除数组中给定索引处的元素,然后重新排列元素。
IntArray.HeapRemoveAt(1); // ==> IntArray == [2,4,4,6,9,5,8,10,7]

// 使用 HeapTop 查看堆的顶部节点,无需变更数组。
int32 Top = IntArray.HeapTop();  // ==> Top == 2

TMap

  • TMap 是虚幻C++中的散列容器,主要由一个键类型和一个值类型,以关联对TPair<KeyType, ValueType>的形式存储在映射中。
  • 键类型必须支持 GetTypeHash 函数,并提供运算符 == 来比较各个键是否等值。
  • TMap 也可使用任选分配器来控制内存分配行为。

初始化

TMap<int32, FString> FoodMap;  //空
  • 使用结构体当作Key。
// 声明结构体,定义操作符 == 和函数 GetTypeHash。
USTRUCT(BlueprintType)
struct FMyStruct
{
	GENERATED_USTRUCT_BODY()

	EMyEnum EnumA;
	EMyEnum EnumB;

	friend inline bool operator==(const FMyStruct& A, const FMyStruct& B)
	{
		return A.EnumA == B.EnumA && A.EnumB == B.EnumB;
	}

	friend inline uint32 GetTypeHash(const FMyStruct& C)
	{
		uint32 Hash = 0;

		Hash = HashCombine(Hash, GetTypeHash(C.EnumA));
		Hash = HashCombine(Hash, GetTypeHash(C.EnumB));
		return Hash;
	}
};
// 使用 FMyStruct 当 Key。但是不支持蓝图了。
TMap<FMyStruct, FString> MyMap;

TMap<int32, FString> FoodMapA;

// 使用 Add 或 Emplace 添加元素
// 与 TArray 类似,Emplace 可以避免创建不必要的临时变量,效率比 Add 高。
// 各个键都必定是唯一。
// 如果尝试添加重复键,将替换原来的键值。
FoodMapA.Add(3, TEXT("Banana"));
FoodMapA.Add(6, TEXT("Apple"));
/** 当前TMap为:
 * FoodMapA == [
 *  { Key:3, Value:"Banana"},
 *  { Key:6, Value:"Apple" }]
*/
FoodMapA.Add(3, TEXT("Orange"));
FoodMapA.Emplace(7, TEXT("Cake"));
/** 当前TMap为:
 * FoodMapA == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cake" }]
*/

// 使用 Append 函数合并映射,将一个映射的所有元素移至另一个映射。
// Apeend的Map映射的相同键会替代原映射中的键。
/** 存在一个TMap为:
 * FoodMapB == [
 *  { Key:3, Value:"Sweet"},
 *  { Key:1, Value:"Water" }
 *  { Key:9, Value:"Cola" }]
*/

FoodMapA.Append(FoodMapB);
/** 当前TMap为:
 * FoodMapA == [
 *  { Key:3, Value:"Sweet"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cake" },
 *  { Key:1, Value:"Water" },
 *  { Key:9, Value:"Cola" }]
*/

/** 当前TMap为:
 * FoodMap == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cake" }]
*/

// 使用 Remove 函数移除元素。
// 移除元素将在数据结构中留下空位。
FoodMap.Remove(6);
/** 当前TMap为:
 * FoodMap == [
 *  { Key:3, Value:"Orange"},
 *  <invalid>,
 *  { Key:7, Value:"Cake" }]
*/

// 使用 FindAndRemoveChecked 函数可用于从映射移除元素并返回其值,不存在,则出现断言。
FString RemovedStr = FoodMap.FindAndRemoveChecked(7); // ==> RemovedStr == Cake
FString RemovedStr = FoodMap.FindAndRemoveChecked(7); // ==> Assert
/** 当前TMap为:
 * FoodMap == [
 *  { Key:3, Value:"Orange"},
 *  <invalid>,
 *  <invalid>]
*/
 
// 使用 RemoveAndCopyValue 函数的作用与 Remove 相似,不同点是会将已移除元素的值复制到引用。
// 如果映射中不存在指定的键,函数将返回 false。
FString RemovedStrA;
bool bExistA = FoodMap.RemoveAndCopyValue(3, RemovedStrA); // ==> bExistA  == true; RemovedStrA  == "Orange"
/** 当前TMap为:
 * FoodMap == [
 *  <invalid>,
 *  <invalid>,
 *  <invalid>]
*/
FString RemovedStrB;
bool bExistB = FoodMap.RemoveAndCopyValue(1, RemovedStrB); // ==> bExistB  == false; RemovedStrB  == ""
/** 当前TMap为:
 * FoodMap == [
 *  <invalid>,
 *  <invalid>,
 *  <invalid>]
*/

// 使用 Empty 或 Reset 函数可将映射中的所有元素移除。
// Reset 则是尽可能多地留出slack量。
FoodMap.Empty();
/** 当前TMap为:
 * FoodMap == [
 *  <invalid>,
 *  <invalid>,
 *  <invalid>]
*/
FoodMap.Reset();
/** 当前TMap为:
 * FoodMap == []
*/

  • 运算符。
/** 当前TMap为:
 * FoodMap == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cake" }]
*/

// 使用 [] 去更改值。
FoodMap[7] = TEXT("Cola");
/** 当前TMap为:
 * FoodMap == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cola" }]
*/

// 使用 MoveTemp 函数可调用移动语义,在移动后,源映射为空。
TMap<int32, FString> NewFoodMap;
NewFoodMap = MoveTemp(FoodMap);
/** 当前TMap为:
 * NewFoodMap == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cola" }]
 * FoodMap == []
*/
  • 排序。
/** 当前TMap为:
 * FoodMap == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cake" }]
*/

// 使用 KeySort 或 ValueSort 函数可分别按键和值进行排序,皆可使用Lambda谓语。
FoodMap.KeySort([](int32 A, int32 B) {
    return A > B;
});
/** 当前TMap为:
 * FoodMap == [
 *  { Key:7, Value:"Cake" },
 *  { Key:6, Value:"Apple" },
 *  { Key:3, Value:"Orange"}]
*/

FruitMap.ValueSort([](const FString& A, const FString& B) {
    return A.Len() > B.Len();
});
/** 当前TMap为:
 * FoodMap == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cake" }]
*/
  • 内存。
TMap<int32, FString> FoodMap;
/** 当前TMap为:
 * FoodMap == []
*/

// 使用 Reserve 函数预先分配映射
FoodMap.Reserve(3);
/** 当前TMap为:
 * FoodMap == [
 *  <invalid>,
 *  <invalid>,
 *  <invalid>]
*/

// 使用 Shrink 函数可移除 TMap 中的全部 Slack。
FoodMap.Shrink();
/** 当前TMap为:
 * FoodMap == []
*/

// Shrink 将从容器的末端移除所有Slack,通常会留下空白元素。
// 使用 Compact 函数将空白空间组合在一起,为调用 Shrink 做好准备。
/** 假设当前TMap为:
 * FoodMap == [
 *  <invalid>,
 * { Key:3, Value:"Orange"},
 *  <invalid>,
 * { Key:6, Value:"Apple" },
 *  <invalid>,
 * { Key:7, Value:"Cake" }
 *  <invalid>]
*/
FoodMap.Shrink();
/** 当前TMap为:
 * FoodMap == [
 *  <invalid>,
 * { Key:3, Value:"Orange"},
 *  <invalid>,
 * { Key:6, Value:"Apple" },
 *  <invalid>,
 * { Key:7, Value:"Cake" }]
*/
FoodMap.Compact();
/** 当前TMap为:
 * FoodMap == [
 * { Key:3, Value:"Orange"},
 * { Key:6, Value:"Apple" },
 * { Key:7, Value:"Cake" },
 *  <invalid>,
 *  <invalid>,
 *  <invalid>]
*/
FoodMap.Shrink();
/** 当前TMap为:
 * FoodMap == [
 * { Key:3, Value:"Orange"},
 * { Key:6, Value:"Apple" },
 * { Key:7, Value:"Cake" }]
*/

/** 当前TMap为:
 * FoodMap == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"Cake" }]
*/

// 使用 Num 函数查询映射中保存的元素数量。
int32 Count = FoodMap.Num(); // ==> Count == 3

// 使用 Contains 函数查询是否包含特定键。
bool bHas = FoodMap.Contains(7); // ==> bHas == true
bool bHas = FoodMap.Contains(1); // ==> bHas == false

// 使用 [] 运算符将键用作索引查找相应值。
// 在使用运算符 [] 前,应检查映射中是否 Contains 该键,如果映射中键不存在,将触发断言。
FString Str = FoodMap[7]; // ==> Str == "Cake"

// 使用 Find 函数进行键查找,
// 如果映射包含该键,Find 将返回指向元素数值的指针,如果映射不包含该键,则返回 nullptr。
FString* Str = FoodMap.Find(7); // ==> *Str == "Cake"
FString* Str = FoodMap.Find(10); // ==> Str == nullptr

// 使用 FindOrAdd 将返回给定键的值引用。
// 如果映射中不存在该键,FindOrAdd 将返回新创建的元素。
// FindOrAdd 可修改映射,因此仅适用于非常量映射。
FString& Str = FoodMap.FindOrAdd(7);
Str = "";
FString& Str = FoodMap.FindOrAdd(8);
Str = "Cake";

/** 当前TMap为:
 * FoodMapA == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"" },
 *  { Key:8, Value:"Cake" },]
*/

// 使用 FindRef 会返回与给定键关联的值副本。
// 若映射中未找到给定键,则返回默认构建值。
// FindRef 不会创建新元素,因此既可用于常量映射,也可用于非常量映射。
FString Str = FoodMap.FindRef(8); // ==> Str == "Cake"
Str = TEXT("Apple"); // ==> Str == "Apple"

/** 当前TMap为:
 * FoodMapA == [
 *  { Key:3, Value:"Orange"},
 *  { Key:6, Value:"Apple" },
 *  { Key:7, Value:"" },
 *  { Key:8, Value:"Cake" },]
*/

// 使用 FindKey 函数执行逆向查找。
// 返回指向提供值配对的第一个键的指针,搜索映射中不存在的值将返回空键。
// 按值查找比按键查找慢。
const int32* KeyA = FoodMap.FindKey(TEXT("Apple"));   // *KeyA   == 6
const int32* KeyB = FoodMap.FindKey(TEXT("Cola")); //  KeyB == nullptr

// 使用 GenerateKeyArray 和 GenerateValueArray 分别使用所有键和值的副本来填充到 TArray。
TArray<int32>   FoodKeys;
TArray<FString> FoodValues;
FoodMap.GenerateKeyArray(FoodKeys);
FoodMap.GenerateValueArray(FoodValues);
// FoodKeys   == [ 3,6,7,8 ]
// FoodValues == [ "Orange","Apple","Pineapple","Cake" ]
  • 遍历操作。(注意:遍历时不要做删除操作)
// auto 遍历
for (auto& Elem :FoodMap)
{
    Elem.Key;	// Map的Key
	Elem.Value;	// Map的Value
}

// 迭代器遍历
// CreateIterator 返回拥有读写访问权限的迭代器,
// CreateConstIterator 返回拥有只读访问权限的迭代器
for (auto It = FoodMap.CreateConstIterator(); It; ++It)
{
	It.Key();	// Map的Key
	It.Value();	// Map的Value
}

TSet

  • TSet 是一种快速容器类,使用数据值本身作为键。

初始化

TSet<FString> FoodSet;

// 使用 Add 或 Emplace 添加元素。
FoodSet.Add(TEXT("Banana"));
FoodSet.Add(TEXT("Apple"));
FoodSet.Emplace(TEXT("Orange"));
// ==> FoodSet == [ "Banana", "Apple", "Orange" ]

// 使用 Append 函数来插入另一个集合中的所有元素。
// 存在 NewFoodSet == [ "Cake", "Cola", "Orange" ]
FoodSet.Append(NewFoodSet);
// ==> FoodSet == [ "Banana", "Apple", "Orange", "Cake", "Cola" ]

// 存在 FoodSet == [ "Banana", "Apple", "Orange", "Cake", "Cola" ]

// 使用 Remove 函数可按索引移除元素。
int RemovedCount = FoodSet.Remove(TEXT("Apple")); // ==> RemovedCount == 1
// ==> FoodSet == [ "Banana", <invalid>, "Orange", "Cake", "Cola" ]

int RemovedCount = FoodSet.Remove(TEXT("Apple")); // ==> RemovedCount == 0
// ==> FoodSet == [ "Banana", <invalid>, "Orange", "Cake", "Cola" ]

// 使用 Empty 或 Reset 函数可将集合中的所有元素移除。
FoodSet.Reset();
// ==> FoodSet == [ <invalid>, <invalid>, <invalid>, <invalid>, <invalid> ]

FoodSet.Empty();
// ==> FoodSet == []

  • 排序。
// 存在 FoodSet == [ "Apple", "Orange", "Cake", ]
FoodSet.Sort([](const FString& A, const FString& B) {
    return A > B;
});
// ==> FoodSet == [ "Apple", "Cake", "Orange"]

FoodSet.Sort([](const FString& A, const FString& B) {
    return A.Len() > B.Len();
});
// ==> FoodSet == [ "Orange", "Apple", "Cake"]
  • 内存
// 使用 Reserve 函数可直接创建 slack,在添加元素之前预分配内存。
TSet<int32> IntSet;
IntSet.Reserve(4); // ==> IntSet == [ <invalid>, <invalid>, <invalid>, <invalid> ]
// 使用 Shrink 函数可移除 TSet 中的全部 slack。
IntSet.Shrink(); // ==> IntSet == []

// 存在 FoodSet == [ "Apple", <invalid>, "Orange", <invalid>, "Cake", <invalid>]
// 使用 Compact 或 CompactStable 函数,将空白空间组合在一起,为调用 Shrink 做好准备。
FoodSet.Shrink(); // ==> IntSet == [ "Apple", <invalid>, "Orange", <invalid>, "Cake"]
FoodSet.Compact(); // ==> IntSet == [ "Apple", "Orange", "Cake", <invalid>, <invalid>]
FoodSet.Shrink(); // ==> IntSet == [ "Apple", "Orange", "Cake" ]

// 存在 FoodSet == [ "Banana", "Apple", "Orange", "Cake", "Cola"]

// 使用 Num 函查询集合中保存的元素数量。
int32 Count = FoodSet.Num(); // ==> Count == 5

// 使用 Contains 函数查询是否包含特定元素。
bool bContains = FoodSet.Contains(TEXT("Banana")); // ==> bContains == true
bool bContains = FoodSet.Contains(TEXT("Water"));  // ==> bContains == false

// 使用 FSetElementId 结构体可查找集合中某个键的索引。
// 使用运算符 [] 查找元素。
FSetElementId Index = FoodSet.Index(TEXT("Banana")); // ==> FoodSet[Index] == TEXT("Banana")
FSetElementId Index = FoodSet.Index(TEXT("Water"));  // ==> FoodSet[Index] Assert

// 使用 Find 返回指向元素数值的指针。
FString* Ptr = FoodSet.Find(TEXT("Banana")); // ==> *Ptr == TEXT("Banana")
FString* Ptr = FoodSet.Find(TEXT("Water"));  // ==> Ptr == nullptr

// 使用 Array 函数会返回一个 TArray,其中填充了 TSet 中每个元素的一份副本。
TArray<FString> FoodArray = FoodSet.Array();
// FoodArray == [ "Banana","Apple","Orange","Cake","Cola" ]
  • 遍历
// auto遍历。
for (auto& Elem : FoodSet)
{
	Elem; // 遍历的值。
}

// 迭代器遍历。
// CreateIterator 返回拥有读写访问权限的迭代器,
// CreateConstIterator 返回拥有只读访问权限的迭代器
for (auto It = FoodSet.CreateConstIterator(); It; ++It)
{
    *it;	// 遍历的值。
}

运算

// 存在 FoodSetA == [ "Orange", "Apple", "Cake"]
// 存在 FoodSetB == [ "Banana", "Apple", "Cola"]

// 使用 Intersect 求交集。
TSet<FString> FoodSetC = FoodSetA.Intersect(FoodSetB); // ==> FoodSetC == [ "Apple" ]

// 使用 Union 求并集。
TSet<FString> FoodSetD = FoodSetA.Union(FoodSetB); // ==> FoodSetD == [ "Orange", "Apple", "Cake", "Banana", "Cola" ]

// 使用 Difference 求差集,返回 this set 不在 other set 里的集合。
TSet<FString> FoodSetE = FoodSetA.Difference(FoodSetB); // A不在B里 ==> FoodSetD == [ "Orange", "Cake" ]
TSet<FString> FoodSetF = FoodSetB.Difference(FoodSetA); // B不在A里 ==> FoodSetD == [ "Banana", "Cola" ]

// 存在 IntSetA == [ 1,2,3,4 ]
// 存在 IntSetB == [ 1,2,3,4,5,6,7,8,9 ]
// 使用 Includes 计算 this set 是否包含 other set。
bool Includes = IntSetA.Includes(IntSetB); // ==> Includes == false
bool Includes = IntSetB.Includes(IntSetA); // ==> Includes == true

补充 TMultiMap

  • 允许键重复,不能用 UPROPERTY 标记。
    • 否则会报错:Unrecognized type 'TMultiMap' - type must be a UCLASS, USTRUCT or UENUM
TMultiMap<int32, FString> multiMap;

// 使用 Add 添加元素。
multiMap.Add(1, TEXT("苹果"));
/** 当前TMap为:
 * FoodMap == [
 * { Key:1, Value:"苹果"}]
*/
multiMap.Add(1, 456);
/** 当前TMap为:
 * FoodMap == [
 * { Key:1, Value:"苹果"},
 * { Key:1, Value:"香蕉" }]
*/

// 使用 Emplace 添加元素。
multiMap.Emplace(2, TEXT("电脑"));
multiMap.Emplace(2, TEXT("平板"));
multiMap.Emplace(2, TEXT("手机"));
/** 当前TMap为:
 * FoodMap == [
 * { Key:1, Value:"苹果"},
 * { Key:1, Value:"香蕉" },
 * { Key:2, Value:"电脑" },
 * { Key:2, Value:"平板" },
 * { Key:2, Value:"手机" }]
*/

// 使用 Remove 会移除所有重复键元素。
multiMap.Remove(333);
/** 当前TMap为:
 * FoodMap == [
 * { Key:2, Value:"电脑" },
 * { Key:2, Value:"平板" },
 * { Key:2, Value:"手机" }]
*/


// 使用 Remove 依据键和值,移除指定元素。
multiMap.Remove(2, TEXT("电脑")); 
/** 当前TMap为:
 * FoodMap == [
 * { Key:2, Value:"平板" },
 * { Key:2, Value:"手机" }]
*/
// 使用 RemoveSingle 与 Remove一样。
multiMap.RemoveSingle(2, TEXT("手机"));
/** 当前TMap为:
 * FoodMap == [
 * { Key:2, Value:"平板" }]
*/

// 使用 FindPair 查找元素,因为键可重复,所以需要指定值才能找到数据。
int* ptr = multiMap.FindPair(2, TEXT("平板"));
 类似资料: