UE4 C++: TArray container

Table of contents

overview

create

add element

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

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

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.

iteration

range-based for statement

regular for

iterator

to sort

Sort

HeapSort: heap sort (unstable)

StableSort: After sorting, the relative ordering of equivalent elements is guaranteed. StableSort implemented as a merge sort

Inquire

GetData: The function returns a pointer to the elements in the array, which directly accesses the array memory

GetTypeSize: Get the size of a single element

[]: The index operator gets the element and returns a reference that can be used to manipulate the 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;

FindByKey: Compares the element with any object and returns a pointer to the first matched element, or nullptr if no match is found

FindByPredicate: Similar to IndexOfByPredicate, the difference is that its return value is a pointer instead of an index

FilterByPredicate: Get an array of elements that match a specific predicate

remove element

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

operator

+=: It can replace the Append function for array connection

MoveTemp: the contents of one array can be moved to another array, and the source array will be emptied

==, !=: Arrays can be compared

heap

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

HeapRemoveAt: Deletes the element at a given index in the array, then rearranges the elements to maintain the heap

HeapTop: view the top node of the heap without changing the array

Slack

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

Empty()

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

Shrink(): Remove all Slack

raw memory


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 as  Emplace the function, with minor differences:

  • Add(or  Push) 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 literal  FStringand then  FString the contents of that temporary will be moved to new inside the container  FString ; whereas  Emplace a new will be created directly with a string literal  FString. The end result is the same, but  Emplace avoids the creation of temporary files. For  FString non-obvious numeric types such as , temporary files are often more harmful than helpful.

Overall, Emplace better than  Add, 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 be  Add used for shallow types and will  Emplace be used for other types. Emplace is always more efficient than  Add, but  Add 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

Guess you like

Origin blog.csdn.net/Jason6620/article/details/126504319