目录
Append: 可一次性添加其他TArray中的多个元素,或者指向常规C数组的指针及该数组的大小
StableSort:排序后保证等值元素的相对排序。StableSort 作为归并排序实现
GetData:函数返回指向数组中元素的指针,该操作直接访问数组内存
IsValidIndex:询问容器,特定索引是否有效(0≤index())<>
ContainsByPredicate:是否包含与特定谓词匹配的元素
IndexOfByKey:返回首个匹配到的元素的索引;未找到返回INDEX_NONE
IndexOfByPredicate:查找与特定谓词匹配的首个元素的索引;
FindByKey:将元素和任意对象对比,并返回首个匹配到的元素的指针,如果未匹配到,则返回nullptr
FindByPredicate:和IndexOfByPredicate相似,不同的是,它的返回值是指针,而不是索引
FilterByPredicate:可获取与特定谓词匹配的元素数组
RemovSwap、RemoveAtSwap、RemoveAllSwap
MoveTemp:可将一个数组中的内容移动到另一个数组中,源数组将被清空
HeapPush:新元素添加到堆,对其他节点进行重新排序,以对堆进行维护
HeapRemoveAt:删除数组中给定索引处的元素,然后重新排列元素,对堆进行维护
GetSlack:找出数组中的Slack量,相当于Max()-Min()
Reset():与Empty函数类似,不同之处是若当前内存分配已提供请求的Slack,该函数将不释放内存。但若请求的Slack较大,其将分配更多内存
TArray IntArray;
IntArray.Init(10, 5); //==>[10,10,10,10,10]
- TArray
StrArr; - StrArr.Add(TEXT("Hello"));
- IntArray.AddUnique(TEXT("Hello"));
- StrArr.Emplace(TEXT("World")); //==>["Hello","World"]
- FString Arr[] = { TEXT("of"), TEXT("Tomorrow") };
- StrArr.Append(Arr, ARRAY_COUNT(Arr)); //==>["Hello","World","of","Tomorrow"]
新元素添加到数组时,数组的分配器将根据需要分配内存。当前数组大小超出时,默认分配器将添加足够的内存,用于存储多个新元素。
Add
和Emplace
函数的多数效果相同,细微区别在于:
Add
(或Push
)将元素类型的实例复制(或移动)到数组中。
Emplace
使用给定参数构建元素类型的新实例。因此在
TArray
中,Add
将用字符串文字创建临时FString
,然后将该临时FString
的内容移至容器内的新FString
中;而Emplace
将用字符串文字直接新建FString
。最终结果相同,但Emplace
可避免创建临时文件。对于FString
等非浅显数值类型而言,临时文件通常有害无益。总体而言,
Emplace
优于Add
,因此其可避免在调用点创建无需临时变量,并将此类变量复制或移动到容器中。根据经验,可将Add
用于浅显类型,将Emplace
用于其他类型。Emplace
的效率始终高于Add
,但Add
的可读性可能更好。
仅在尚不存在等值元素时,
AddUnique
才会向容器添加新元素。使用以下元素类型的运算符检查等值性:运算符==
:
- StrArr.AddUnique(TEXT("!"));
- // StrArr == ["Hello","World","of","Tomorrow","!"]
-
- StrArr.AddUnique(TEXT("!"));
- // StrArr is unchanged as "!" is already an element
StrArr.Insert(TEXT("Brave"), 1); //==>["Hello","Brave","World","of","Tomorrow","!"]
- StrArr.SetNum(8); //==>["Hello","Brave","World","of","Tomorrow","!","",""]
-
- StrArr.SetNum(6); //==>["Hello","Brave","World","of","Tomorrow","!"]
- FString JoinedStr;
- for (auto& Str :StrArr)
- {
- JoinedStr += Str;
- JoinedStr += TEXT(" ");
- } // JoinedStr == "Hello Brave World of Tomorrow !"
- for (int32 Index = 0; Index != StrArr.Num(); ++Index)
- {
- JoinedStr += StrArr[Index];
- JoinedStr += TEXT(" ");
- }
函数 CreateIterator 和 CreateConstIterator 可分别用于元素的读写和只读访问
- for (auto It = StrArr.CreateConstIterator(); It; ++It)
- {
- JoinedStr += *It;
- JoinedStr += TEXT(" ");
- }
StrArr.Sort(); //==>["!","Brave","Hello","of","Tomorrow","World"]
二进制谓词提供不同排序规则(lambda)
- StrArr.Sort([](const FString& A, const FString& B){
- return A.len() < B.len();
- }); //按字符串长度排序, ==>["!","of","Hello","Brave","World","Tomorrow"]
无论带或不带二元谓词,均可用于执行对排序。是否选择使用它则取决于特定数据和与 Sort 函数之间的排序效率对比。和 Sort 一样,HeapSort 并不稳定
- StrArr.HeapSort([](const FString& A, const FString& B) {
- return A.Len() < B.Len();
- }); //==>["!","of","Hello","Brave","World","Tomorrow"]
- StrArr.StableSort([](const FString& A, const FString& B) {
- return A.Len() < B.Len();
- }); //==>["!","of","Brave","Hello","World","Tomorrow"]
Num:查询元素数量
int32 Count = StrArr.Num(); // Count == 6
仅在数组存在且未执行更改数组的操作时,此指针方有效。仅 StrPtr 的首个 Num 指数才可被解除引用
- FString* StrPtr = StrArr.GetData();
- // StrPtr[0] == "!"
- // StrPtr[1] == "of"
- // ...
- // StrPtr[5] == "Tomorrow"
- // StrPtr[6] - undefined behavior
uint32 ElementSize = StrArr.GetTypeSize(); // ElementSize == sizeof(FString)
- FString Elem1 = StrArr[1]; // Elem1 == "of"
-
- StrArr[3] = StrArr[3].ToUpper(); //==>["!","of","Brave","HELLO","World","Tomorrow"]
- 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
- 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"
- bool bHello = StrArr.Contains(TEXT("Hello")); // bHello == true
- bool bGoodbye = StrArr.Contains(TEXT("Goodbye")); // bGoodbye == false
- 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
- 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)
- int32 IndexLast;
- if (StrArr.FindLast(TEXT("Hello"), IndexLast)){
- // IndexLast == 3, because there aren't any duplicates
- }
-
- int32 IndexLast2 = StrArr.FindLast(TEXT("Hello")); // IndexLast2 == 3
- int32 Index = StrArr.IndexOfByPredicate([](const FString& Str){
- return Str.Contains(TEXT("r"));
- }); // Index == 2
- auto* OfPtr = StrArr.FindByKey(TEXT("of"))); // OfPtr == &StrArr[1]
- auto* ThePtr = StrArr.FindByKey(TEXT("the"))); // ThePtr == nullptr
- 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
- auto Filter = StrArray.FilterByPredicate([](const FString& Str){
- return !Str.IsEmpty() && Str[0] < TEXT('M');
- });
- TArray
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]
ValArr.RemoveSingle(30); //==>[10,5,10,15,25,30]
- ValArr.RemoveAt(2); // 移除下标为2的元素, ==>[10,5,15,25,30]
- ValArr.RemoveAt(99); // 引发错误,越界
- ValArr.RemoveAll([](int32 Val) {
- return Val % 3 == 0; }); //移除为3倍数的所有数值, ==> [10,5,25]
移动过程存在开销。如不需要剩余元素排序,可使用 RemoveSwap、RemoveAtSwap 和 RemoveAllSwap 函数减少此开销。此类函数的工作方式与其非交换变种相似,不同之处在于其不保证剩余元素的排序,因此可更快地完成任务:
- TArray
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]
ValArr2.Empty(); //==>[]
ValArr2.Reset (); //==>[]
数组是常规数值类型,可使用标准复制构造函数或赋值运算符进行复制。由于数组严格拥有其元素,复制数组的操作是深层的,因此新数组将拥有其自身的元素副本
- TArray
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];
ValArr4 += ValArr3; //==>[5,2,3,1,2,3]
ValArr3 = MoveTemp(ValArr4); // ValArr3 == [5,2,3,1,2,3]; ValArr4 == []
元素的排序很重要:只有元素的顺序和数量相同时,两个数组才被视为相同
- TArray
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,因为两个数组内的元素顺序不同
TArray 拥有支持二叉堆数据结构的函数。堆是一种二叉树,其中父节点的排序等于或高于其子节点。作为数组实现时,树的根节点位于元素0,索引N处节点的左右子节点的指数分别为2N+1和2N+2。子节点彼此间不存在特定排序。
- TArray
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]
这两个函数的区别在于前者引用元素的类型来返回顶部元素的副本,而后者只是简单地移除顶部节点,不进行任何形式的返回。两个函数得出的数组变更一致,重新正确排序其他元素可对堆进行维护
- int32 TopNode;
- HeapArr.HeapPop(TopNode); // TopNode == 1; HeapArr == [2,3,4,6,4,5,8,10,7,9]
HeapArr.HeapRemoveAt(1); // HeapArr == [2,4,4,6,9,5,8,10,7]
int32 Top = HeapArr.HeapTop(); // Top == 2
因为数组的尺寸可进行调整,因此它们使用的是可变内存量。为避免每次添加元素时需要重新分配,分配器通常会提供比需求更多的内存,使之后进行的Add调用不会因为重新分配而出现性能损失。同样,删除元素通常不会释放内存。
- TArray
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
例如,如需要向数组添加大约100个新元素,则可在添加前确保拥有可至少存储100个新元素的Slack,以便添加新元素时无需分配内存。上文所述的 Empty
函数接受可选Slack参数
- 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
- SlackArray.Reset(0);
- // SlackArray.GetSlack() == 3
- // SlackArray.Num() == 0
- // SlackArray.Max() == 3
- SlackArray.Reset(10);
- // SlackArray.GetSlack() == 10
- // SlackArray.Num() == 0
- // SlackArray.Max() == 10
此函数将把分配重新调整为所需要的大小,使其保存当前的元素序列,而无需实际移动元素
- 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
参考链接: