TArray<int32> IntArray;
IntArray.Init(10, 5);
TArray<FString> StrArr; StrArr.Add (TEXT("Hello")); StrArr.Emplace(TEXT("World")); // StrArr == ["Hello","World"]
Add
Emplace這里效果一樣 Emplace效率更高,減少中間臨時變量
FString Arr[] = { TEXT("of"), TEXT("Tomorrow") }; StrArr.Append(Arr, ARRAY_COUNT(Arr)); // StrArr == ["Hello","World","of","Tomorrow"]
StrArr.AddUnique(TEXT("!")); // StrArr == ["Hello","World","of","Tomorrow","!"] StrArr.AddUnique(TEXT("!")); // StrArr is unchanged as "!" is already an element
StrArr.Insert(TEXT("Brave"), 1); // StrArr == ["Hello","Brave","World","of","Tomorrow","!"]
SetNum可以數組擴容,超出個數補空
StrArr.SetNum(8); // StrArr == ["Hello","Brave","World","of","Tomorrow","!","",""]
也可以減容,達到刪除元素目的
StrArr.SetNum(6); // StrArr == ["Hello","Brave","World","of","Tomorrow","!"]
Iteration
There are several ways to iterate over the elements of your array, but the recommended way is to use C++'s ranged-for feature:
FString JoinedStr; for (auto& Str : StrArr) { JoinedStr += Str; JoinedStr += TEXT(" "); } // JoinedStr == "Hello Brave World of Tomorrow ! "
Regular index-based iteration is also possible of course:
for (int32 Index = 0; Index != StrArr.Num(); ++Index) { JoinedStr += StrArr[Index]; JoinedStr += TEXT(" "); }
Finally, arrays also have their own iterator type for more control over your iteration. There are two methods called CreateIterator and CreateConstIterator which can be used for read-write or read-only access to the elements respectively:
for (auto It = StrArr.CreateConstIterator(); It; ++It) { JoinedStr += *It; JoinedStr += TEXT(" "); }
Sorting
Arrays can be sorted simply by calling the Sort method:
StrArr.Sort(); // StrArr == ["!","Brave","Hello","of","Tomorrow","World"]
StrArr.Sort([](const FString& A, const FString& B) { return A.Len() < B.Len(); }); // StrArr == ["!","of","Hello","Brave","World","Tomorrow"]
Test:
數組 合並&排序
TArray<FString> StrArr; StrArr.Add (TEXT("Hello")); StrArr.Emplace(TEXT("World")); FString a[] = {TEXT("b"), TEXT("a"), TEXT("d"), TEXT("c")}; StrArr.Append(a, ARRAY_COUNT(a)); StrArr.Sort([](const FString& A, const FString& B) { return A.Len() > B.Len(); });
遍歷數組元素並打印到屏幕上
for (auto It = StrArr.CreateConstIterator(); It; ++ It) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Array test1 :%s"), **It)); }
FString JoinedStr; for (auto& Str : StrArr) { JoinedStr += Str; JoinedStr += TEXT(" "); } UE_LOG(YourLog, Warning, TEXT("Array test2 :%s"), *JoinedStr);
for (int32 Index = 0; Index != StrArr.Num(); ++Index) { UE_LOG(YourLog, Warning, TEXT("Array test3 :%s"), *(StrArr[Index])); }
Exchange
交換數組元素
Exchange(FlavorArr2[0], FlavorArr2[1]);
Queries
1 得到數組元素數量 Num
int32 Count = StrArr.Num();
2 C-style API獲得指向數組首元素地址的指針
FString* StrPtr = StrArr.GetData(); // StrPtr[0] == "!" // StrPtr[1] == "of" // ... // StrPtr[5] == "Tomorrow" // StrPtr[6] - undefined behavior
3 得到數組元素類型大小
uint32 ElementSize = StrArr.GetTypeSize(); // ElementSize == sizeof(FString)
4 獲取元素 數組下標
FString Elem1 = StrArr[1]; // Elem1 == "of"
5 判斷下標是否有效
bool bValidM1 = StrArr.IsValidIndex(-1); bool bValid0 = StrArr.IsValidIndex(0); bool bValid5 = StrArr.IsValidIndex(5); bool bValid6 = StrArr.IsValidIndex(6); // bValidM1 == false // bValid0 == true // bValid5 == true // bValid6 == false
6 operator[] returns a reference, so it can also be used to mutate the elements inside the array, assuming your array isn't const:
StrArr[3] = StrArr[3].ToUpper(); // StrArr == ["!","of","Brave","HELLO","World","Tomorrow"]
7 獲取最后元素 Last & Top Last 和 Top (不帶參數) 相同 Last() == Last(0) == Top() Last(1) 獲得倒數第二個元素
FString ElemEnd = StrArr.Last(); FString ElemEnd0 = StrArr.Last(0); FString ElemEnd1 = StrArr.Last(1); FString ElemTop = StrArr.Top(); // ElemEnd == "Tomorrow" // ElemEnd0 == "Tomorrow" // ElemEnd1 == "World" // ElemTop == "Tomorrow"
8 是否包含 Contains & ContainsByPredicate
bool bHello = StrArr.Contains(TEXT("Hello")); bool bGoodbye = StrArr.Contains(TEXT("Goodbye")); // bHello == true // bGoodbye == false
bool bLen5 = StrArr.ContainsByPredicate([](const FString& Str){ return Str.Len() == 5; }); bool bLen6 = StrArr.ContainsByPredicate([](const FString& Str){ return Str.Len() == 6; }); // bLen5 == true // bLen6 == false
9 Find & FindLast
int32 Index; if (StrArr.Find(TEXT("Hello"), Index)) { // Index == 3 }
int32 IndexLast; if (StrArr.FindLast(TEXT("Hello"), IndexLast)) { // IndexLast == 3, because there aren't any duplicates }
如果不傳第二個參數,則返回值為待查元素的index,如果該元素不存在,則返回INDEX_NONE
int32 Index2 = StrArr.Find(TEXT("Hello")); int32 IndexLast2 = StrArr.FindLast(TEXT("Hello")); int32 IndexNone = StrArr.Find(TEXT("None")); // Index2 == 3 // IndexLast2 == 3 // IndexNone == INDEX_NONE
int32 Index = StrArr.IndexOfByKey(TEXT("Hello")); // Index == 3
10 IndexOfByKey & IndexOfByPredicate
int32 Index = StrArr.IndexOfByPredicate([](const FString& Str){ return Str.Contains(TEXT("r")); }); // Index == 2
11 FindByKey:Instead of returning indices, we can get pointers back to the found elements instead. FindByKey works like IndexOfByKey by comparing the elements to an arbitrary object, but returns a pointer to the found element. nullptr is returned if none was found:
auto* OfPtr = StrArr.FindByKey(TEXT("of"))); auto* ThePtr = StrArr.FindByKey(TEXT("the"))); // OfPtr == &StrArr[1] // ThePtr == nullptr
12 FindByPredicate : Likewise, FindByPredicate can be used like IndexOfByPredicate, except a pointer is returned instead of an index:
auto* Len5Ptr = StrArr.FindByPredicate([](const FString& Str){ return Str.Len() == 5; }); auto* Len6Ptr = StrArr.FindByPredicate([](const FString& Str){ return Str.Len() == 6; }); // Len5Ptr == &StrArr[2] // Len6Ptr == nullptr
13 FilterByPredicate:返回過濾數組
auto Filter = StrArray.FilterByPredicate([](const FString& Str){ return !Str.IsEmpty() && Str[0] < TEXT('M'); });
Removal
Remove中字符串判斷是否相同不區分大小寫
StrArr.Remove(TEXT("hello")); // StrArr == ["!","of","Brave","World","Tomorrow"] StrArr.Remove(TEXT("goodbye")); // StrArr is unchanged, as it doesn't contain "goodbye"
Note that "HELLO" was removed even though we asked it to remove "hello". Equality is tested via the element type's operator==; remember for FString, this is a case-insensitive comparison.
The final element of an array can be removed by the Pop method:
移除最后一個
StrArr.Pop();
Remove可以移除所有相同元素
TArray<int32> ValArr; int32 Temp[] = { 10, 20, 30, 5, 10, 15, 20, 25, 30 }; ValArr.Append(Temp, ARRAY_COUNT(Temp)); // ValArr == [10,20,30,5,10,15,20,25,30] ValArr.Remove(20); // ValArr == [10,30,5,10,15,25,30]
如果只想移除第一個相同的元素
ValArr.RemoveSingle(30); // ValArr == [10,5,10,15,25,30]
移除某位置元素
ValArr.RemoveAt(2); // Removes the element at index 2 // ValArr == [10,5,15,25,30] ValArr.RemoveAt(99); // This will cause a runtime error as // there is no element at index 99
帶條件的移除
ValArr.RemoveAll([](int32 Val) { return Val % 3 == 0; }); // ValArr == [10,5,25]
更有效率的刪除,刪除后剩余元素不能保證在原位置
TArray<int32> ValArr2; for (int32 i = 0; i != 10; ++i) ValArr2.Add(i % 5); // ValArr2 == [0,1,2,3,4,0,1,2,3,4] ValArr2.RemoveSwap(2); // ValArr2 == [0,1,4,3,4,0,1,3] ValArr2.RemoveAtSwap(1); // ValArr2 == [0,3,4,3,4,0,1] ValArr2.RemoveAllSwap([](int32 Val) { return Val % 3 == 0; }); // ValArr2 == [1,4,4]
清空
ValArr2.Empty(); // ValArr2 == []
Operators
數組數據是基礎數據 數字 字符串等 數組賦值給另一個數組變量,深復制,兩個數組是獨立的
TArray<int32> ValArr3; ValArr3.Add(1); ValArr3.Add(2); ValArr3.Add(3); auto ValArr4 = ValArr3; // ValArr4 == [1,2,3]; ValArr4[0] = 5; // ValArr3 == [1,2,3]; // ValArr4 == [5,2,3];
數組可以使用+操作符合並
ValArr4 += ValArr3; // ValArr4 == [5,2,3,1,2,3]
MoveTemp 轉移數組
ValArr3 = MoveTemp(ValArr4); // ValArr3 == [5,2,3,1,2,3] // ValArr4 == []
== 和 != 可以比較數組,當且僅當數組元素數量相同 順序相同的時候兩個數組 == 比較才為true
TArray<FString> FlavorArr1; FlavorArr1.Emplace(TEXT("Chocolate")); FlavorArr1.Emplace(TEXT("Vanilla")); // FlavorArr1 == ["Chocolate","Vanilla"] auto FlavorArr2 = Str1Array; // 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, because FString comparison ignores case Exchange(FlavorArr2[0], FlavorArr2[1]); // FlavorArr2 == ["VANILLA","CHOCOLATE"] bool bComparison3 = FlavorArr1 == FlavorArr2; // bComparison3 == false, because the order has changed
Heap 數組可以轉換成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]
HeapArr.HeapPush(4); // HeapArr == [1,2,4,3,4,5,8,10,7,9,6]
eapArr.HeapRemoveAt(1); // HeapArr == [2,4,4,6,9,5,8,10,7]
HeapPop(可帶參數) == HeapPopDiscard(不帶參數)
int32 TopNode; HeapArr.HeapPop(TopNode); // TopNode == 1 // HeapArr == [2,3,4,6,4,5,8,10,7,9]
int32 Top = HeapArr.HeapTop(); // Top == 2
Slack
數組是可變的,為避免每增加一個元素就要給數組重新分配內存,數組都有一定冗余空間,GetSlack 獲取冗余個數,Num 當前數量 Max數組最大大小
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
注意這個細節可以提高效率,當我們知道要添加元素的數量,可以重置數組
用Empty (可帶一個參數,設置Max)
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 不清理內存,但是可以擴容
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 移除冗余內存空間
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
AddUninitialized & InsertUninitialized
數組開辟一塊新的內存空間
int32 SrcInts[] = { 2, 3, 5, 7 }; TArray<int32> UninitInts; UninitInts.AddUninitialized(4); FMemory::Memcpy(UninitInts.GetData(), SrcInts, 4*sizeof(int32)); // UninitInts == [2,3,5,7]
TArray<FString> UninitStrs; UninitStrs.Emplace(TEXT("A")); UninitStrs.Emplace(TEXT("D")); UninitStrs.InsertUninitialized(1, 2); new ((void*)(UninitStrs.GetData() + 1)) FString(TEXT("B")); new ((void*)(UninitStrs.GetData() + 2)) FString(TEXT("C")); // UninitStrs == ["A","B","C","D"]
AddZeroed & InsertZeroed (默認值初始化 0 或者空根據類型而定)
struct S { S(int32 InInt, void* InPtr, float InFlt) : Int(InInt) , Ptr(InPtr) , Flt(InFlt) { } int32 Int; void* Ptr; float Flt; }; TArray<S> SArr; SArr.AddZeroed(); // SArr == [{ Int: 0, Ptr: nullptr, Flt: 0.0f }]
SetNumUninitialized & SetNumZeroed
SArr.SetNumUninitialized(3); new ((void*)(SArr.GetData() + 1)) S(5, (void*)0x12345678, 3.14); new ((void*)(SArr.GetData() + 2)) S(2, (void*)0x87654321, 2.72); // SArr == [ // { Int: 0, Ptr: nullptr, Flt: 0.0f }, // { Int: 5, Ptr: 0x12345678, Flt: 3.14f }, // { Int: 2, Ptr: 0x87654321, Flt: 2.72f } // ] SArr.SetNumZeroed(5); // SArr == [ // { Int: 0, Ptr: nullptr, Flt: 0.0f }, // { Int: 5, Ptr: 0x12345678, Flt: 3.14f }, // { Int: 2, Ptr: 0x87654321, Flt: 2.72f }, // { Int: 0, Ptr: nullptr, Flt: 0.0f }, // { Int: 0, Ptr: nullptr, Flt: 0.0f } // ]
使用Uninitialized or Zeroed會有警告提醒,元素類型應該為不可變,否則會有invalid array elements and undefined錯誤,數組最好為固定類型,如FMatrix || FVector
Misc
不是逐元素序列化,而是整塊內存二進制序列化,如果數組內容是內建類型或者扁平數據結構,可以提高數組序列化效率。
CountBytes && GetAllocatedSize 獲取內存大小
Swap && SwapMemory 功能相同 except Swap does some extra error checking on the indices and will assert if the indices are out of range.