UE4 C++:TMap容器

目录

概述

创建

添加元素

Add:

Emplace:代替 Add,防止插入映射时创建临时文件

Append:合并映射,将一个映射的所有元素移至另一个映射,源映射的相同键会替代目标映射中的键

迭代

范围for

迭代器

查询

Num:查询映射中保存的元素数量

Contains:查询是否包含特定键

[]:运算符将键用作索引查找相应值

Find:进行键查找

FindOrAdd:返回对与给定键关联的值的引用

FindRef:返回与给定键关联的值副本

FindKey:执行逆向查找

GenerateKeyArray和GenerateValueArray:分别使用所有键和值的副本来填充 TArray

移除

Remove:函数提供要移除元素的键

FindAndRemoveChecked:可用于从映射移除元素并返回其值

RemoveAndCopyValue:与 Remove 相似,不同点是会将已移除元素的值复制到引用参数。如果映射中不存在指定的键,则输出参数将保持不变,函数将返回 false。

Empty / Reset:可将映射中的所有元素移除

排序

运算符

Slack

KeyFuncs


概述

  • TMap主要由两个类型定义(一个键类型和一个值类型),以关联对的形式存储在映射中。

  • 将数据存储为键值对(TPair<KeyType, ValueType>),只将键用于存储和获取

  • 映射有两种类型:TMap 和 TMultiMap

    • TMap 中的键是唯一的
    • TMultiMap 可存储多个相同的键
  • TMap 也是值类型,支持通常的复制、赋值和析构函数运算,以及它的元素的强所有权。在映射被销毁时,它的元素都会被销毁。键和值也必须为值类型。

  • TMap 是散列容器,这意味着键类型必须支持 GetTypeHash 函数,并提供 运算符== 来比较各个键是否等值

  • TMap 也可使用任选分配器来控制内存分配行为。但不同于 TArray,这些是集合分配器,而不是 fheapAllocator 和 TInlineAllocator 之类的标准UE4分配器。集合分配器(TSetAllocator类)定义映射应使用的散列桶数量,以及应使用哪个标准UE4分配器来存储散列和元素。

  • KeyFuncs 是最后一个 TMap 模板参数,该参数告知映射如何从元素类型获取键,如何比较两个键是否相等,以及如何对键进行散列计算。这些参数有认值,它们只会返回对键的引用,使用 运算符== 确定相等性,并调用非成员 GetTypeHash 函数进行散列计算。如果您的键类型支持这些函数,可使用它作为映射键,不需要提供自定义 KeyFuncs。

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

创建

TMap<int32, FString> FruitMap; //空TMap,此时尚未分配内存

添加元素

Add:

  • 元素按插入顺序排列,但不保证这些元素在内存中实际保留此排序
  • 各个键都必定是唯一。如果尝试添加重复键,将替换原来的键值
  • Add 函数可接受不带值的键。调用此重载后的 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")

迭代器

  • CreateIterator 返回拥有读写访问权限的迭代器,
  • CreateConstIterator 返回拥有只读访问权限的迭代器
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:返回对与给定键关联的值的引用

  • 如果映射中不存在该键,FindOrAdd 将返回新创建的元素(使用给定键和认构建值),该元素也会被添加到映射。
  • 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:函数提供要移除元素的键

  • 返回值是被移除元素的数量
  • 如果映射不包含与键匹配的元素,则返回值可为零。
  • 移除元素将在数据结构(在Visual Studio的观察窗口中可视化映射时可看到)中留下空位
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

参考链接

【UE4 C++ 基础知识】<6> 容器——TMap - 砥才人 - 博客园 (cnblogs.com)

Map Containers in Unreal Engine | 虚幻引擎5.0文档

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...