Table of contents
Init: fill multiple identical values
Add: Duplicate elements can be added, and temporary variables will be created and copied when adding
AddUnique: Duplicate elements cannot be added
Emplace: No temporary variables are created when adding, and the performance is better than Add
Insert: Adds a single element or a copy of an array of elements at a given index
SetNum: The function can directly set the number of array elements.
HeapSort: heap sort (unstable)
GetTypeSize: Get the size of a single element
IsValidIndex: Asks the container if a particular index is valid (0≤index())<>
Last: The reverse index from the end of the array, the default is 0
Top: Returns the last element, does not accept an index
Contains: Whether to contain specific elements
ContainsByPredicate: Whether to contain elements matching a specific predicate
Find: Whether the element exists and returns the index of the first element found
FindLast: Whether the element exists and returns the index of the last element found
IndexOfByKey: returns the index of the first matched element; returns INDEX_NONE if not found
IndexOfByPredicate: Find the index of the first element matching a specific predicate;
FilterByPredicate: Get an array of elements that match a specific predicate
Remove: Remove elements from the array
RemoveSingle: removes the first matching element in the array
RemoveAt: Remove elements by index
RemoveAll: Remove elements that match the predicate
RemovSwap、RemoveAtSwap、RemoveAllSwap
Empty: Remove all elements in the array
Reset: Similar to Empty, this function will not release memory
+=: It can replace the Append function for array connection
==, !=: Arrays can be compared
Heapify: converts an existing array into a heap
HeapPush: New elements are added to the heap, and other nodes are reordered to maintain the heap
HeapPop, HeadPopDiscard: Remove the top node of the heap
HeapTop: view the top node of the heap without changing the array
GetSlack: Find out the amount of Slack in the array, equivalent to Max()-Min()
Max: Get the maximum number of elements that the array can hold before the container is reallocated
overview
- TArray is the most commonly used container class in UE. Fast speed, low memory consumption, high security
- It is not designed to consider the expansion problem, so it is recommended not to use new and delete to create and destroy TArray instances in actual operation
- When the TArray element is a numeric type, its elements will also be destroyed when it is destroyed. If a TArray variable is created in another TArray, its elements will be copied into the new variable and state will not be shared
create
TArray<int32> IntArray;
add element
Init: fill multiple identical values
IntArray.Init(10, 5); //==>[10,10,10,10,10]
Add: Duplicate elements can be added, and temporary variables will be created and copied when adding
AddUnique: Duplicate elements cannot be added
Emplace: No temporary variables are created when adding, and the performance is better than Add
TArray<FString> StrArr;
StrArr.Add(TEXT("Hello"));
IntArray.AddUnique(TEXT("Hello"));
StrArr.Emplace(TEXT("World")); //==>["Hello","World"]
Append: Multiple elements in other TArrays can be added at one time, or a pointer to a regular C array and the size of the array
FString Arr[] = { TEXT("of"), TEXT("Tomorrow") };
StrArr.Append(Arr, ARRAY_COUNT(Arr)); //==>["Hello","World","of","Tomorrow"]
The array's allocator allocates memory as needed when new elements are added to the array. When the current array size is exceeded, the default allocator will add enough memory to store multiple new elements.
Add
Has most of the same effects asEmplace
the function, with minor differences:
Add
(orPush
) Copy (or move) an instance of the element type into the array.
Emplace
Constructs a new instance of the element type with the given parameters.So in
TArray<FString>
,Add
a temporary will be created with a string literalFString
and thenFString
the contents of that temporary will be moved to new inside the containerFString
; whereasEmplace
a new will be created directly with a string literalFString
. The end result is the same, butEmplace
avoids the creation of temporary files. ForFString
non-obvious numeric types such as , temporary files are often more harmful than helpful.Overall,
Emplace
better thanAdd
, so it avoids creating unnecessary temporary variables at the call site, and copying or moving such variables into the container. As a rule of thumb, use will beAdd
used for shallow types and willEmplace
be used for other types.Emplace
is always more efficient thanAdd
, butAdd
may be more readable.
A new element is added to the container only if an equivalent element does not already exist
AddUnique
. Check for equality using the following element-type operators:运算符==
:
StrArr.AddUnique(TEXT("!"));
// StrArr == ["Hello","World","of","Tomorrow","!"]
StrArr.AddUnique(TEXT("!"));
// StrArr is unchanged as "!" is already an element
Insert: Adds a single element or a copy of an array of elements at a given index
StrArr.Insert(TEXT("Brave"), 1); //==>["Hello","Brave","World","of","Tomorrow","!"]
SetNum: The function can directly set the number of array elements.
- If the new number is greater than the current number, use the default constructor of the element type to create a new element
- If the new number is less than the current number, SetNum will remove the element.
StrArr.SetNum(8); //==>["Hello","Brave","World","of","Tomorrow","!","",""]
StrArr.SetNum(6); //==>["Hello","Brave","World","of","Tomorrow","!"]
iteration
range-based for statement
FString JoinedStr;
for (auto& Str :StrArr)
{
JoinedStr += Str;
JoinedStr += TEXT(" ");
} // JoinedStr == "Hello Brave World of Tomorrow !"
regular for
for (int32 Index = 0; Index != StrArr.Num(); ++Index)
{
JoinedStr += StrArr[Index];
JoinedStr += TEXT(" ");
}
iterator
The functions CreateIterator and CreateConstIterator can be used for read-write and read-only access to elements respectively
for (auto It = StrArr.CreateConstIterator(); It; ++It)
{
JoinedStr += *It;
JoinedStr += TEXT(" ");
}
to sort
Sort
StrArr.Sort(); //==>["!","Brave","Hello","of","Tomorrow","World"]
Binary predicates provide different collations (lambdas)
StrArr.Sort([](const FString& A, const FString& B){
return A.len() < B.len();
}); //按字符串长度排序, ==>["!","of","Hello","Brave","World","Tomorrow"]
HeapSort: heap sort (unstable)
Can be used to perform pair sorting with or without binary predicates. Whether you choose to use it depends on the specific data and sorting efficiency compared with the Sort function. Like Sort, HeapSort is not stable
StrArr.HeapSort([](const FString& A, const FString& B) {
return A.Len() < B.Len();
}); //==>["!","of","Hello","Brave","World","Tomorrow"]
StableSort: After sorting, the relative ordering of equivalent elements is guaranteed. StableSort implemented as a merge sort
StrArr.StableSort([](const FString& A, const FString& B) {
return A.Len() < B.Len();
}); //==>["!","of","Brave","Hello","World","Tomorrow"]
Inquire
Num: query the number of elements
int32 Count = StrArr.Num(); // Count == 6
GetData: The function returns a pointer to the elements in the array, which directly accesses the array memory
This pointer is valid only as long as the array exists and no operations have been performed that alter the array. Only the first Num index of StrPtr can be dereferenced
FString* StrPtr = StrArr.GetData();
// StrPtr[0] == "!"
// StrPtr[1] == "of"
// ...
// StrPtr[5] == "Tomorrow"
// StrPtr[6] - undefined behavior
GetTypeSize: Get the size of a single element
uint32 ElementSize = StrArr.GetTypeSize(); // ElementSize == sizeof(FString)
[]: The index operator gets the element and returns a reference that can be used to manipulate the element
FString Elem1 = StrArr[1]; // Elem1 == "of"
StrArr[3] = StrArr[3].ToUpper(); //==>["!","of","Brave","HELLO","World","Tomorrow"]
IsValidIndex: Asks the container if a particular index is valid (0≤index<Num())
bool bValidM1 = StrArr.IsValidIndex(-1);// bValidM1 == false
bool bValid0 = StrArr.IsValidIndex(0); // bValid0 == true
bool bValid5 = StrArr.IsValidIndex(5); // bValid5 == true
bool bValid6 = StrArr.IsValidIndex(6); // bValid6 == false
Last: The reverse index from the end of the array, the default is 0
Top: Returns the last element, does not accept an index
FString ElemEnd = StrArr.Last(); // ElemEnd == "Tomorrow"
FString ElemEnd0 = StrArr.Last(0); // ElemEnd0 == "Tomorrow"
FString ElemEnd1 = StrArr.Last(1); // ElemEnd1 == "World"
FString ElemTop = StrArr.Top(); // ElemTop == "Tomorrow"
Contains: Whether to contain specific elements
bool bHello = StrArr.Contains(TEXT("Hello")); // bHello == true
bool bGoodbye = StrArr.Contains(TEXT("Goodbye")); // bGoodbye == false
ContainsByPredicate: Whether to contain elements matching a specific predicate
bool bLen5 = StrArr.ContainsByPredicate([](const FString& Str){
return Str.Len() == 5;
}); // bLen5 == true
bool bLen6 = StrArr.ContainsByPredicate([](const FString& Str){
return Str.Len() == 6;
}); // bLen6 == false
Find: Whether the element exists and returns the index of the first element found
int32 Index;
if (StrArr.Find(TEXT("Hello"), Index)){
// Index == 3
}
int32 Index2 = StrArr.Find(TEXT("Hello")); // Index2 == 3
int32 IndexNone = StrArr.Find(TEXT("None")); // IndexNone == INDEX_NONE(实质上是-1)
FindLast: Whether the element exists and returns the index of the last element found
int32 IndexLast;
if (StrArr.FindLast(TEXT("Hello"), IndexLast)){
// IndexLast == 3, because there aren't any duplicates
}
int32 IndexLast2 = StrArr.FindLast(TEXT("Hello")); // IndexLast2 == 3
IndexOfByKey: returns the index of the first matched element; returns INDEX_NONE if not found
IndexOfByPredicate: Find the index of the first element matching a specific predicate;
int32 Index = StrArr.IndexOfByPredicate([](const FString& Str){
return Str.Contains(TEXT("r"));
}); // Index == 2
FindByKey: Compares the element with any object and returns a pointer to the first matched element, or nullptr if no match is found
auto* OfPtr = StrArr.FindByKey(TEXT("of"))); // OfPtr == &StrArr[1]
auto* ThePtr = StrArr.FindByKey(TEXT("the"))); // ThePtr == nullptr
FindByPredicate: Similar to IndexOfByPredicate, the difference is that its return value is a pointer instead of an index
auto* Len5Ptr = StrArr.FindByPredicate([](const FString& Str){
return Str.Len() == 5;
}); // Len5Ptr == &StrArr[2]
auto* Len6Ptr = StrArr.FindByPredicate([](const FString& Str){
return Str.Len() == 6;
}); // Len6Ptr == nullptr
FilterByPredicate: Get an array of elements that match a specific predicate
auto Filter = StrArray.FilterByPredicate([](const FString& Str){
return !Str.IsEmpty() && Str[0] < TEXT('M');
});
remove element
Remove: Remove elements from the array
TArray<int32> ValArr;
int32 Temp[] = { 10, 20, 30, 5, 10, 15, 20, 25, 30 };
ValArr.Append(Temp, ARRAY_COUNT(Temp)); //==>[10,20,30,5,10,15,20,25,30]
ValArr.Remove(20); //==>[10,30,5,10,15,25,30]
RemoveSingle: removes the first matching element in the array
ValArr.RemoveSingle(30); //==>[10,5,10,15,25,30]
RemoveAt: Remove elements by index
ValArr.RemoveAt(2); // 移除下标为2的元素, ==>[10,5,15,25,30]
ValArr.RemoveAt(99); // 引发错误,越界
RemoveAll: Remove elements that match the predicate
ValArr.RemoveAll([](int32 Val) {
return Val % 3 == 0; }); //移除为3倍数的所有数值, ==> [10,5,25]
RemovSwap、RemoveAtSwap、RemoveAllSwap
There is overhead in the moving process. If ordering of the remaining elements is not required, this overhead can be reduced using the RemoveSwap, RemoveAtSwap, and RemoveAllSwap functions. Such functions work similarly to their noncommutative variants, except that they do not guarantee the ordering of the remaining elements and thus complete the task more quickly:
TArray<int32> ValArr2;
for (int32 i = 0; i != 10; ++i)
ValArr2.Add(i % 5); //==>[0,1,2,3,4,0,1,2,3,4]
ValArr2.RemoveSwap(2); //==>[0,1,4,3,4,0,1,3]
ValArr2.RemoveAtSwap(1); //==>[0,3,4,3,4,0,1]
ValArr2.RemoveAllSwap([](int32 Val) {
return Val % 3 == 0; }); //==>[1,4,4]
Empty: Remove all elements in the array
ValArr2.Empty(); //==>[]
Reset: Similar to Empty, this function will not release memory
ValArr2.Reset (); //==>[]
operator
Arrays are regular numeric types that can be copied using the standard copy constructor or assignment operator. Since an array strictly owns its elements, the operation of copying an array is deep , so the new array will have its own copy of the elements
TArray<int32> ValArr3;
ValArr3.Add(1);
ValArr3.Add(2);
ValArr3.Add(3);
auto ValArr4 = ValArr3; // ValArr4 == [1,2,3];
ValArr4[0] = 5; // ValArr4 == [5,2,3]; ValArr3 == [1,2,3];
+=: It can replace the Append function for array connection
ValArr4 += ValArr3; //==>[5,2,3,1,2,3]
MoveTemp: the contents of one array can be moved to another array, and the source array will be emptied
ValArr3 = MoveTemp(ValArr4); // ValArr3 == [5,2,3,1,2,3]; ValArr4 == []
==, !=: Arrays can be compared
The ordering of the elements is important: two arrays are considered identical only if the order and number of elements are the same
TArray<FString> FlavorArr1;
FlavorArr1.Emplace(TEXT("Chocolate"));
FlavorArr1.Emplace(TEXT("Vanilla")); // FlavorArr1 == ["Chocolate","Vanilla"]
auto FlavorArr2 = FlavorArr1; // FlavorArr2 == ["Chocolate","Vanilla"]
bool bComparison1 = FlavorArr1 == FlavorArr2; // bComparison1 == true
for ( auto& Str : FlavorArr2 )
{
Str = Str.ToUpper();
} // FlavorArr2 == ["CHOCOLATE","VANILLA"]
bool bComparison2 = FlavorArr1 == FlavorArr2; // bComparison2 == true,因为FString的对比忽略大小写
Exchange(FlavorArr2[0], FlavorArr2[1]); // FlavorArr2 == ["VANILLA","CHOCOLATE"]
bool bComparison3 = FlavorArr1 == FlavorArr2; // bComparison3 == false,因为两个数组内的元素顺序不同
heap
TArray has functions to support the binary heap data structure. A heap is a type of binary tree in which a parent node is ordered equal to or higher than its children. When implemented as an array, the root of the tree is at element 0, and the left and right children of the node at index N have indices 2N+1 and 2N+2, respectively. There is no specific ordering of child nodes relative to each other.
Heapify: converts an existing array into a heap
TArray<int32> HeapArr;
for (int32 Val = 10; Val != 0; --Val){
HeapArr.Add(Val);
} // HeapArr == [10,9,8,7,6,5,4,3,2,1]
HeapArr.Heapify(); // HeapArr == [1,2,4,3,6,5,8,10,7,9]
HeapPush: New elements are added to the heap, and other nodes are reordered to maintain the heap
HeapArr.HeapPush(4); // HeapArr == [1,2,4,3,4,5,8,10,7,9,6]
HeapPop, HeadPopDiscard: Remove the top node of the heap
The difference between these two functions is that the former refers to the type of the element to return a copy of the top element, while the latter simply removes the top node without any return. The array changes obtained by the two functions are consistent, and reordering other elements correctly can maintain the heap
int32 TopNode;
HeapArr.HeapPop(TopNode); // TopNode == 1; HeapArr == [2,3,4,6,4,5,8,10,7,9]
HeapRemoveAt: Deletes the element at a given index in the array, then rearranges the elements to maintain the heap
HeapArr.HeapRemoveAt(1); // HeapArr == [2,4,4,6,9,5,8,10,7]
HeapTop: view the top node of the heap without changing the array
int32 Top = HeapArr.HeapTop(); // Top == 2
Slack
Because arrays can be resized, they use a variable amount of memory. To avoid the need to reallocate each time an element is added, the allocator usually provides more memory than required, so that subsequent Add calls will not suffer a performance penalty due to reallocation. Also, removing elements usually does not free memory.
- The difference between the number of existing elements in the container and the number of elements that can be added before the next allocation is Slack
- The array built by default does not allocate memory, and slack is initially 0
GetSlack: Find out the amount of Slack in the array, equivalent to Max()-Min()
Max: Get the maximum number of elements that the array can hold before the container is reallocated
- The allocator determines the amount of Slack in the container after reallocation, so Slack is not constant
TArray<int32> SlackArray;
// SlackArray.GetSlack() == 0
// SlackArray.Num() == 0
// SlackArray.Max() == 0
SlackArray.Add(1);
// SlackArray.GetSlack() == 3
// SlackArray.Num() == 1
// SlackArray.Max() == 4
SlackArray.Add(2);
SlackArray.Add(3);
SlackArray.Add(4);
SlackArray.Add(5);
// SlackArray.GetSlack() == 17
// SlackArray.Num() == 5
// SlackArray.Max() == 22
- Although there is no need to manage Slack, Slack can be managed to optimize the array to meet the needs
Empty()
For example, if you need to add about 100 new elements to an array, you can make sure you have Slack that can store at least 100 new elements before adding them so that you don't need to allocate memory when you add them. The functions described above Empty
accept an optional Slack parameter
SlackArray.Empty();
// SlackArray.GetSlack() == 0
// SlackArray.Num() == 0
// SlackArray.Max() == 0
SlackArray.Empty(3);
// SlackArray.GetSlack() == 3
// SlackArray.Num() == 0
// SlackArray.Max() == 3
SlackArray.Add(1);
SlackArray.Add(2);
SlackArray.Add(3);
// SlackArray.GetSlack() == 0
// SlackArray.Num() == 3
// SlackArray.Max() == 3
Reset(): Similar to the Empty function, the difference is that if the current memory allocation has provided the requested Slack, this function will not release the memory. But if the requested Slack is larger, it will allocate more memory
SlackArray.Reset(0);
// SlackArray.GetSlack() == 3
// SlackArray.Num() == 0
// SlackArray.Max() == 3
SlackArray.Reset(10);
// SlackArray.GetSlack() == 10
// SlackArray.Num() == 0
// SlackArray.Max() == 10
Shrink(): Remove all Slack
This function will resize the allocation to the desired size, allowing it to hold the current sequence of elements without actually moving the elements
SlackArray.Add(5);
SlackArray.Add(10);
SlackArray.Add(15);
SlackArray.Add(20);
// SlackArray.GetSlack() == 6
// SlackArray.Num() == 4
// SlackArray.Max() == 10
SlackArray.Shrink();
// SlackArray.GetSlack() == 0
// SlackArray.Num() == 4
// SlackArray.Max() == 4
raw memory
Reference link:
[UE4 C++ Basics] <5> Container——TArray
Array Containers in Unreal Engine | Unreal Engine 5.0 Documentation