Map Containers in Unreal Engine
source: https://dev.epicgames.com/documentation/en-us/unreal-engine/map-containers-in-unreal-engine?application_version=5.8 version: Unreal Engine 5.8 Documentation fetched: 2026-10-11(curl 抓取页面文本,去掉 HTML;原文全文,供 wiki 页面引用)
Map Containers in Unreal Engine | Unreal Engine 5.8 Documentation | Epic Developer Community Table of Contents Developer TMap TMap TMaps are defined by two types, a key type and a value type, which are stored as associated pairs in the map.
On this page After TArray , the most commonly used container in Unreal Engine is TMap .
TMap is similar to TSet in that its structure is based on hashing keys.
However, unlike TSet , TMap stores data as key-value pairs ( TPair<KeyType, ValueType> ), using keys only for storage and retrieval.
Types of Maps in Unreal Engine There are two types of map in Unreal Engine: TMap TMultiMap Overview of TMap In a TMap, key-value pairs are treated as the element type of the map as if each pair were an individual object.
In this document, element means a key-value pair, while individual components are referred to as the element's key or the element's value.
The element type is a TPair<KeyType, ElementType> , though it should be rare to need to refer to the TPair type directly.
TMap keys are unique.
Similar to TArray, TMap is a homogeneous container, meaning that all of its elements are strictly the same type.
TMap is a value type, and supports the usual copy, assignment, and destructor operations, as well as strong ownership of its elements, which are destroyed when the map is destroyed.
The key and value must also be value types.
TMap is a hashing container, which means that the key type must support the GetTypeHash function and provide an operator== for comparing keys for equality TMap and TMultimap (like many Unreal Engine containers) assume that the element type is trivially relocatable, meaning that elements can safely be moved from one location in memory to another by directly copying raw bytes.
Overview of TMultiMap Supports storing multiple, identical keys.
When adding a new key-value pair to a TMap with a key that matches an existing pair, the new pair will replace the old one.
In a TMultiMap, the container stores both the new pair and the old.
TMap can take an optional allocator to control the memory allocation behavior.
However, unlike TArray, these are set allocators rather than the standard Unreal allocators like FHeapAllocator and TInlineAllocator .
Set Allocators , ( TSetAllocator ), define how many hash buckets the map should use and which standard UE allocators should be used for hash and element storage.
The final TMap template parameter is KeyFuncs , which tells the map how to retrieve the key from the element type, how to compare two keys for equality, and how to hash the key.
These have defaults that return a reference to the key, then use operator== for equality, and call the non-member GetTypeHash function for hashing.
If your key type supports these functions, you can use it as a map key without supplying a custom KeyFuncs .
Unlike TArray, the relative order of TMap elements in memory is not reliable or stable, and iterating over the elements is likely to return them in a different order from the order in which they were added.
Elements are unlikely to be laid out contiguously in memory.
The base data structure of a map is a sparse array, which is an array that efficiently supports gaps between its elements.
As elements are removed from the map, gaps in the sparse array will appear.
Adding new elements to the array can then fill these gaps.
However, even though TMap doesn't shuffle elements to fill gaps, pointers to map elements may still be invalidated, as the entire storage can be reallocated when it is full and new elements are added.
Create and Fill a Map The following code creates a TMap : C++ TMap<int32, FString> FruitMap; TMap<int32, FString> FruitMap; Copy full snippet (1 line long) FruitMap is now an empty TMap of strings that are identified by integer keys.
We have specified neither an allocator nor a KeyFuncs , so the map performs standard heap allocation and compares the key of type int32 using operator== and hashes the key using GetTypeHash .
No memory has been allocated at this point.
Add The standard way to populate a map is to call Add with a key and a value: C++ 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(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" } // ] Copy full snippet (8 lines long) While the elements are listed here in the order of insertion, there is no guarantee of their actual order in memory.
For a new map, they are likely to be in order of insertion, but as more insertions and removals happen, it becomes increasingly unlikely that new elements will appear at the end.
This is not a TMultiMap ; therefore, the keys are guaranteed to be unique.
The following is the result of attempting to add a duplicate key: C++ FruitMap.Add(2, TEXT("Pear")); // FruitMap == [ // { Key: 5, Value: "Banana" }, // { Key: 2, Value: "Pear" }, // { Key: 7, Value: "Pineapple" } // ] FruitMap.Add(2, TEXT("Pear")); // FruitMap == [ // { Key: 5, Value: "Banana" }, // { Key: 2, Value: "Pear" }, // { Key: 7, Value: "Pineapple" } // ] Copy full snippet (6 lines long) The map still contains three elements, but the previous Grapefruit value with a key of 2 has been replaced with Pear.
The Add function can accept a key without a value.
When this overloaded Add is called, the value will be default-constructed: C++ FruitMap.Add(4); // FruitMap == [ // { Key: 5, Value: "Banana" }, // { Key: 2, Value: "Pear" }, // { Key: 7, Value: "Pineapple" }, // { Key: 4, Value: "" } // ] FruitMap.Add(4); // FruitMap == [ // { Key: 5, Value: "Banana" }, // { Key: 2, Value: "Pear" }, // { Key: 7, Value: "Pineapple" }, // { Key: 4, Value: "" } // ] Copy full snippet (7 lines long) Emplace Like TArray, we can use Emplace instead of Add to avoid the creation of temporaries when inserting into the map: C++ 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" } // ] 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" } // ] Copy full snippet (8 lines long) Here, the key and value are passed directly to their respective type constructors.
While this isn't meaningful for the int32 key, it does avoid the creation of a temporary FString for the value.
Unlike TArray, it's only possible to emplace elements into a map with single-argument constructors.
Append You can merge two maps with the Append function, which moves all elements from the argument map into the calling object map: C++ 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" }, Expand code Copy full snippet (14 lines long) In the above example, the resulting map is equivalent to using Add or Emplace to add each element of FruitMap2 individually, emptying FruitMap2 when the process is complete.
This means that any element from FruitMap2 that shares its key with an element already in FruitMap replaces that element.
If you mark the TMap with the UPROPERTY macro and one of the "editable" keywords ( EditAnywhere , EditDefaultsOnly , or EditInstanceOnly ), you can add and edit elements in the Editor .
C++ UPROPERTY(EditAnywhere, Category = MapsAndSets) TMap<int32, FString> FruitMap; UPROPERTY(EditAnywhere, Category = MapsAndSets) TMap<int32, FString> FruitMap; Copy full snippet (2 lines long) Iterate Iteration over TMaps is similar to TArrays.
You can use the C++ ranged-for feature, remembering that the element type is a TPair: C++ for (auto& Elem : FruitMap) { FPlatformMisc::LocalPrint( *FString::Printf( TEXT("(%d, "%s")\n"), Elem.Key, *Elem.Value ) ); } Expand code Copy full snippet (18 lines long) You can create iterators with the CreateIterator and CreateConstIterator functions.
Function Description CreateIterator Return an iterator with read-write access.
CreateConstIterator Returns a read-only iterator.
In either case, you can use the Key and Value functions of these iterators to examine the elements.
Printing the contents of our example FruitMap using iterators would look like this: C++ 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 ) ); } 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 ) ); } Copy full snippet (10 lines long) Get Value If you know that your map contains a certain key, you can look up the corresponding value with operator[] , using the key as the index.
Doing this with a non-const map returns a non-const reference, while a const map returns a const reference.
You should always check that the map contains the key before using operator[] .
If the map does not contain the key, it will assert.
C++ FString Val7 = FruitMap[7]; // Val7 == "Pineapple" FString Val8 = FruitMap[8]; // Assert!
FString Val7 = FruitMap[7]; // Val7 == "Pineapple" FString Val8 = FruitMap[8]; // Assert!
Copy full snippet (4 lines long) Query To determine how many elements are currently in a TMap, call the Num function: C++ int32 Count = FruitMap.Num(); // Count == 6 int32 Count = FruitMap.Num(); // Count == 6 Copy full snippet (2 lines long) To determine whether or not a map contains a specific key, call the Contains function: C++ bool bHas7 = FruitMap.Contains(7); bool bHas8 = FruitMap.Contains(8); // bHas7 == true // bHas8 == false bool bHas7 = FruitMap.Contains(7); bool bHas8 = FruitMap.Contains(8); // bHas7 == true // bHas8 == false Copy full snippet (4 lines long) If you are uncertain whether or not your map contains a key, you could check using the Contains function, and then use operator[] .
However, this is suboptimal, since a successful retrieval involves two lookups on the same key.
The Find function combines these behaviors with a single lookup.
Find returns a pointer to the value of the element if the map contains the key, or a null pointer if it does not.
Calling Find on a const map returns a const pointer.
C++ FString Ptr7 = FruitMap.Find(7); FString Ptr8 = FruitMap.Find(8); // Ptr7 == "Pineapple" // Ptr8 == nullptr FString Ptr7 = FruitMap.Find(7); FString Ptr8 = FruitMap.Find(8); // Ptr7 == "Pineapple" // Ptr8 == nullptr Copy full snippet (4 lines long) Alternatively, to ensure that you receive a valid result from your query, you can use FindOrAdd or FindRef : Function Description FindOrAdd Return a reference to the value associated with the key you provide.
If the key is not in the map, FindOrAdd returns a newly-created element, with your key and the default-constructed value, that it will add to the map.
FindOrAdd is only available for non-const maps.
FindRef Despite its name, returns a copy of the value associated with your key, or a default-constructed value if your key is not found in the map.
FindRef does not create a new element, and thus is available for use with both const and non-const maps.
Because FindOrAdd and FindRef succeed even when the key isn't found in the map, you can safely call them without the usual safety procedures like checking Contains in advance, or null-checking the return value.
C++ 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" } // ] Expand code Copy full snippet (36 lines long) Because FindOrAdd can add new entries to the map, as it does when initializing Ref8 in our example, previously-obtained pointers or references could become invalid.
This is a result of the addition operation allocating memory and moving existing data if the map's backend storage needs to expand to contain the new element.
In the example above, Ref7 may be invalidated after Ref8 after the call to FindOrAdd(8) .
The FindKey function performs a reverse lookup, meaning that a supplied value is matched to a key, and returns a pointer to the first key that's paired to the provided value.
Searching for a value that isn't present in the map returns a null pointer.
C++ const int32 KeyMangoPtr = FruitMap.FindKey(TEXT("Mango")); const int32 KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat")); // KeyMangoPtr == 5 // KeyKumquatPtr == nullptr const int32 KeyMangoPtr = FruitMap.FindKey(TEXT("Mango")); const int32 KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat")); // *KeyMangoPtr == 5 // KeyKumquatPtr == nullptr Copy full snippet (4 lines long) Lookups by value are slower (linear time) than lookups by key.
This is because the map is hashed by key, not by value.
In addition, if a map has multiple keys with the same value, FindKey may return any of them.
The GenerateKeyArray and GenerateValueArray functions populate a TArray with a copy of all the keys and values respectively.
In both cases, the array being passed is emptied before population, so the resulting number of elements will always equal the number of elements in the map.
C++ TArray