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.