目录
Append:合并映射,将一个映射的所有元素移至另一个映射,源映射的相同键会替代目标映射中的键
GenerateKeyArray和GenerateValueArray:分别使用所有键和值的副本来填充 TArray
FindAndRemoveChecked:可用于从映射移除元素并返回其值
RemoveAndCopyValue:与 Remove 相似,不同点是会将已移除元素的值复制到引用参数。如果映射中不存在指定的键,则输出参数将保持不变,函数将返回 false。
概述
-
将数据存储为键值对(
TPair<KeyType, ValueType>
),只将键用于存储和获取 -
映射有两种类型:
TMap
和TMultiMap
- TMap 中的键是唯一的
- TMultiMap 可存储多个相同的键
-
TMap 也是值类型,支持通常的复制、赋值和析构函数运算,以及它的元素的强所有权。在映射被销毁时,它的元素都会被销毁。键和值也必须为值类型。
-
TMap 也可使用任选分配器来控制内存分配行为。但不同于 TArray,这些是集合分配器,而不是 fheapAllocator 和 TInlineAllocator 之类的标准UE4分配器。集合分配器(
TSetAllocator
类)定义映射应使用的散列桶数量,以及应使用哪个标准UE4分配器来存储散列和元素。 -
KeyFuncs 是最后一个 TMap 模板参数,该参数告知映射如何从元素类型获取键,如何比较两个键是否相等,以及如何对键进行散列计算。这些参数有默认值,它们只会返回对键的引用,使用 运算符== 确定相等性,并调用非成员 GetTypeHash 函数进行散列计算。如果您的键类型支持这些函数,可使用它作为映射键,不需要提供自定义 KeyFuncs。
-
与 TArray 不同的是,内存中 TMap 元素的相对排序既不可靠也不稳定,对这些元素进行迭代很可能会使它们返回的顺序和它们添加的顺序有所不同。这些元素也不太可能在内存中连续排列。映射的支持数据结构是稀疏数组,这种数组可有效支持元素之间的空位。当元素从映射中被移除时,稀疏数组中就会出现空位。将新的元素添加到数组可填补这些空位。但是,即便 TMap 不会打乱元素来填补空位,指向映射元素的指针仍然可能失效,因为如果存储器被填满,又添加了新的元素,整个存储可能会重新分配。
创建
TMap<int32, FString> FruitMap; //空TMap,此时尚未分配内存
添加元素
Add:
FruitMap.Add(5, TEXT("Banana"));
FruitMap.Add(2, TEXT("Grapefruit"));
FruitMap.Add(7, TEXT("Pineapple"));
// FruitMap == [
// { Key:5, Value:"Banana" },
// { Key:2, Value:"Grapefruit" },
// { Key:7, Value:"Pineapple" } ]
FruitMap.Add(2, TEXT("Pear"));
// FruitMap == [
// { Key:5, Value:"Banana" },
// { Key:2, Value:"Pear" },
// { Key:7, Value:"Pineapple" }
// ]
FruitMap.Add(4);
// FruitMap == [
// { Key:5, Value:"Banana" },
// { Key:2, Value:"Pear" },
// { Key:7, Value:"Pineapple" },
// { Key:4, Value:"" }
// ]
Emplace:代替 Add,防止插入映射时创建临时文件
FruitMap.Emplace(3, TEXT("Orange"));
// FruitMap == [
// { Key:5, Value:"Banana" },
// { Key:2, Value:"Pear" },
// { Key:7, Value:"Pineapple" },
// { Key:4, Value:"" },
// { Key:3, Value:"Orange" }
// ]
Append:合并映射,将一个映射的所有元素移至另一个映射,源映射的相同键会替代目标映射中的键
TMap<int32, FString> FruitMap2;
FruitMap2.Emplace(4, TEXT("Kiwi"));
FruitMap2.Emplace(9, TEXT("Melon"));
FruitMap2.Emplace(5, TEXT("Mango"));
FruitMap.Append(FruitMap2);
// FruitMap == [
// { Key:5, Value:"Mango" },
// { Key:2, Value:"Pear" },
// { Key:7, Value:"Pineapple" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" }
// ]
// FruitMap2 is Now empty.
迭代
范围for
for (auto& Elem :FruitMap)
{
FPlatformMisc::LocalPrint( *FString::Printf(TEXT("(%d, \"%s\")\n"), Elem.Key, *Elem.Value) );
}
// Output:
// (5, "Mango")
// (2, "Pear")
// (7, "Pineapple")
// (4, "Kiwi")
// (3, "Orange")
// (9, "Melon")
迭代器
for (auto It = FruitMap.CreateConstIterator(); It; ++It)
{
FPlatformMisc::LocalPrint(
*FString::Printf( TEXT("(%d, \"%s\")\n"),
It.Key(), // same as It->Key
*It.Value() // same as *It->Value
) );
}
查询
Num:查询映射中保存的元素数量
int32 Count = FruitMap.Num(); // Count == 6
Contains:查询是否包含特定键
bool bHas7 = FruitMap.Contains(7); // bHas7 == true
bool bHas8 = FruitMap.Contains(8); // bHas8 == false
[]:运算符将键用作索引查找相应值
- 使用非常量映射执行该操作将返回非常量引用,使用常量映射将返回常量引用。
- 在使用 运算符[] 前,应检查映射中是否存在该键。如果映射中键不存在,将触发断言
FString Val7 = FruitMap[7]; // Val7 == "Pineapple"
FString Val8 = FruitMap[8]; // Assert!
Find:进行键查找
- 如果映射包含该键,Find 将返回指向元素数值的指针;如果映射不包含该键,则返回null。
- 在常量映射上调用 Find,返回的指针也将为常量。
FString* Ptr7 = FruitMap.Find(7); // *Ptr7 == "Pineapple"
FString* Ptr8 = FruitMap.Find(8); // Ptr8 == nullptr
FindOrAdd:返回对与给定键关联的值的引用
FString& Ref7 = FruitMap.FindOrAdd(7);
// Ref7 == "Pineapple"
// FruitMap == [
// { Key:5, Value:"Mango" },
// { Key:2, Value:"Pear" },
// { Key:7, Value:"Pineapple" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" }
// ]
FString& Ref8 = FruitMap.FindOrAdd(8);
// Ref8 == ""
// FruitMap == [
// { Key:5, Value:"Mango" },
// { Key:2, Value:"Pear" },
// { Key:7, Value:"Pineapple" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" },
// { Key:8, Value:"" }
// ]
//如已发生重新分配,此处的 Ref7 引用可能会因 FruitMap.FindOrAdd(8) 的调用而无效化。
FindRef:返回与给定键关联的值副本
- 若映射中未找到给定键,则返回默认构建值。
- FindRef 不会创建新元素,因此既可用于常量映射,也可用于非常量映射。
FString Val7 = FruitMap.FindRef(7);
FString Val6 = FruitMap.FindRef(6);
// Val7 == "Pineapple"
// Val6 == ""
// FruitMap == [
// { Key:5, Value:"Mango" },
// { Key:2, Value:"Pear" },
// { Key:7, Value:"Pineapple" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" },
// { Key:8, Value:"" }
// ]
FindKey:执行逆向查找
- 返回指向与所提供值配对的第一个键的指针。搜索映射中不存在的值将返回空键。
- 按值查找比按键查找慢(线性时间)。这是因为映射按键排序,而非按值排序。
- 如果映射有多个具有相同值的键,FindKey 可返回其中任一键。
const int32* KeyMangoPtr = FruitMap.FindKey(TEXT("Mango")); // *KeyMangoPtr == 5
const int32* KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat")); // KeyKumquatPtr == nullptr
GenerateKeyArray和GenerateValueArray:分别使用所有键和值的副本来填充 TArray
TArray<int32> FruitKeys;
TArray<FString> FruitValues;
FruitKeys.Add(999);
FruitKeys.Add(123);
FruitMap.GenerateKeyArray (FruitKeys);
FruitMap.GenerateValueArray(FruitValues);
// FruitKeys == [ 5,2,7,4,3,9,8 ]
// FruitValues == [ "Mango","Pear","Pineapple","Kiwi","Orange","Melon","" ]
移除
Remove:函数提供要移除元素的键
FruitMap.Remove(8);
// FruitMap == [
// { Key:5, Value:"Mango" },
// { Key:2, Value:"Pear" },
// { Key:7, Value:"Pineapple" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" }
// ]
FindAndRemoveChecked:可用于从映射移除元素并返回其值
- 名称中的checked部分意味着将检查键是否存在,如果不存在,则出现断言:
FString Removed7 = FruitMap.FindAndRemoveChecked(7);
// Removed7 == "Pineapple"
// FruitMap == [
// { Key:5, Value:"Mango" },
// { Key:2, Value:"Pear" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" }
// ]
FString Removed8 = FruitMap.FindAndRemoveChecked(8);
// Assert!
RemoveAndcopyValue:与 Remove 相似,不同点是会将已移除元素的值复制到引用参数。如果映射中不存在指定的键,则输出参数将保持不变,函数将返回 false。
FString Removed;
bool bFound2 = FruitMap.RemoveAndcopyValue(2, Removed);
// bFound2 == true
// Removed == "Pear"
// FruitMap == [
// { Key:5, Value:"Mango" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" } ]
bool bFound8 = FruitMap.RemoveAndcopyValue(8, Removed);
// bFound8 == false
// Removed == "Pear", i.e. unchanged
// FruitMap == [
// { Key:5, Value:"Mango" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" } ]
Empty / Reset:可将映射中的所有元素移除
- Empty 可采用参数指示映射中保留的slack量
- Reset 则是尽可能多地留出slack量。
TMap<int32, FString> FruitMapcopy = FruitMap;
// FruitMapcopy == [
// { Key:5, Value:"Mango" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" },
// { Key:9, Value:"Melon" }
// ]
FruitMapcopy.Empty(); // We Could also have called Reset() here.
// FruitMapcopy == []
排序
TMap 可以进行排序。排序后,迭代映射会以排序的顺序显示元素,但下次修改映射时,排序可能会发生变化。排序是不稳定的,因此等值元素在MultiMap中可能以任何顺序出现
使用 KeySort
或 ValueSort
函数可分别按键和值进行排序。两个函数均使用二元谓词来进行排序:
FruitMap.KeySort([](int32 A, int32 B) {
return A > B; // sort keys in reverse
});
// FruitMap == [
// { Key:9, Value:"Melon" },
// { Key:5, Value:"Mango" },
// { Key:4, Value:"Kiwi" },
// { Key:3, Value:"Orange" }
// ]
FruitMap.ValueSort([](const FString& A, const FString& B) {
return A.Len() < B.Len(); // sort strings by length
});
// FruitMap == [
// { Key:4, Value:"Kiwi" },
// { Key:5, Value:"Mango" },
// { Key:9, Value:"Melon" },
// { Key:3, Value:"Orange" }
// ]
运算符
和 TArray 一样,TMap 是常规值类型,可通过标准复制构造函数或赋值运算符进行复制。因为映射严格拥有其元素,复制映射的操作是深层的,所以新的映射将拥有其自己的元素副本。
TMap<int32, FString> NewMap = FruitMap;
NewMap[5] = "Apple";
NewMap.Remove(3);
// FruitMap == [
// { Key:4, Value:"Kiwi" },
// { Key:5, Value:"Mango" },
// { Key:9, Value:"Melon" },
// { Key:3, Value:"Orange" }
// ]
// NewMap == [
// { Key:4, Value:"Kiwi" },
// { Key:5, Value:"Apple" },
// { Key:9, Value:"Melon" }
// ]
MoveTemp
函数可调用移动语义。在移动后,源映射必定为空
FruitMap = MoveTemp(NewMap);
// FruitMap == [
// { Key:4, Value:"Kiwi" },
// { Key:5, Value:"Apple" },
// { Key:9, Value:"Melon" }
// ]
// NewMap == []
Slack
KeyFuncs
参考链接: