UE5 C++ TMap

概述

  • 映射的元素类型为键值对,元素类型实际上是 TPair<KeyType, ElementType>,只将键用于存储和获取
  • TMapTMultiMap 两者之间的不同点是TMap中的键是唯一的,而TMultiMap可存储多个相同的键
  • TMap是散列容器,这意味着键类型必须支持GetTypeHash函数,并提供运算符==来比较各个键是否等值

创建

  TMap<int32, FString> FruitMap;

添加元素

  • Add 函数

    • 添加重复键,覆盖已有的
    • 可接受不带值的键,调用此重载后的 Add 时,值将被默认构建:
    FruitMap.Add(5, TEXT("Banana"));
        FruitMap.Add(2, TEXT("Grapefruit"));
        FruitMap.Add(7, TEXT("Pineapple"));
        // FruitMap == [
        //  { Key:5, Value:"Banana"     },
        //  { Key:2, Value:"Grapefruit" },
        //  { Key:7, Value:"Pineapple"  }
        // ]
    
     FruitMap.Add(2, TEXT("Pear"));
        // FruitMap == [
        //  { Key:5, Value:"Banana"    },
        //  { Key:2, Value:"Pear"      },
        //  { Key:7, Value:"Pineapple" }
        // ]
    FruitMap.Add(4);
        // FruitMap == [
        //  { Key:5, Value:"Banana"    },
        //  { Key:2, Value:"Pear"      },
        //  { Key:7, Value:"Pineapple" },
        //  { Key:4, Value:""          }
        // ]
    
  • Emplace代替Add,防止插入映射时创建临时文件

    FruitMap.Emplace(3, TEXT("Orange"));
        // FruitMap == [
        //  { Key:5, Value:"Banana"    },
        //  { Key:2, Value:"Pear"      },
        //  { Key:7, Value:"Pineapple" },
        //  { Key:4, Value:""          },
        //  { Key:3, Value:"Orange"    }
        // ]
    
  • Append 函数合并映射,将一个映射的所有元素移至另一个映射

     TMap<int32, FString> FruitMap2;
        FruitMap2.Emplace(4, TEXT("Kiwi"));
        FruitMap2.Emplace(9, TEXT("Melon"));
        FruitMap2.Emplace(5, TEXT("Mango"));
        FruitMap.Append(FruitMap2);
        // FruitMap == [
        //  { Key:5, Value:"Mango"     },
        //  { Key:2, Value:"Pear"      },
        //  { Key:7, Value:"Pineapple" },
        //  { Key:4, Value:"Kiwi"      },
        //  { Key:3, Value:"Orange"    },
        //  { Key:9, Value:"Melon"     }
        // ]
        // FruitMap2 is now empty.
    

迭代

  • 范围 for-range

     for (auto& Elem :FruitMap)
        {
            FPlatformMisc::LocalPrint(
                *FString::Printf(
                    TEXT("(%d, \"%s\")\n"),
                    Elem.Key,
                    *Elem.Value
                )
            );
        }
        // Output:
        // (5, "Mango")
        // (2, "Pear")
        // (7, "Pineapple")
        // (4, "Kiwi")
        // (3, "Orange")
        // (9, "Melon")
    
  • 迭代器

    • CreateIterator 返回拥有读写访问权限的迭代器
    • CreateConstIterator 返回拥有只读访问权限的迭代器
     for (auto It = FruitMap.CreateConstIterator(); It; ++It)
        {
            FPlatformMisc::LocalPrint(
                *FString::Printf(
                    TEXT("(%d, \"%s\")\n"),
                    It.Key(),   // same as It->Key
                    *It.Value() // same as *It->Value
                )
            );
        }
    

查询

  • Num函数查询map中保存的元素数量

     int32 Count = FruitMap.Num();     // Count == 6
    
  • Contains函数确定映射是否包含特定键

     bool bHas7 = FruitMap.Contains(7);   // bHas7 == true
     bool bHas8 = FruitMap.Contains(8);    // bHas8 == false
    
  • 运算符[]获取特定键对应的值

        FString Val7 = FruitMap[7];    // Val7 == "Pineapple"
        FString Val8 = FruitMap[8];   // Assert!
    
  • Find 返回指向特定键对应值的指针,如果map不包含该键,则返回nullptr

     FString* Ptr7 = FruitMap.Find(7);   // *Ptr7 == "Pineapple"
     FString* Ptr8 = FruitMap.Find(8);   //  Ptr8 == nullptr
    
  • FindOrAdd将返回对与给定键关联的值的引用

    • 如果映射中不存在该键,FindOrAdd 将返回新创建的元素(使用给定键和默认构建值),该元素也会被添加到映射
    • 仅适用于非常量映射
  • FindRef会返回与给定键对应值的副本

    • 若映射中未找到给定键,则返回默认构建值
    • 不会创建新元素,因此既可用于常量映射,也可用于非常量映射
    FString& Ref7 = FruitMap.FindOrAdd(7);
        // Ref7     == "Pineapple"
        // FruitMap == [
        //  { Key:5, Value:"Mango"     },
        //  { Key:2, Value:"Pear"      },
        //  { Key:7, Value:"Pineapple" },
        //  { Key:4, Value:"Kiwi"      },
        //  { Key:3, Value:"Orange"    },
        //  { Key:9, Value:"Melon"     }
        // ]
        FString& Ref8 = FruitMap.FindOrAdd(8);
        // Ref8     == ""
        // FruitMap == [
        //  { Key:5, Value:"Mango"     },
        //  { Key:2, Value:"Pear"      },
        //  { Key:7, Value:"Pineapple" },
        //  { Key:4, Value:"Kiwi"      },
        //  { Key:3, Value:"Orange"    },
        //  { Key:9, Value:"Melon"     },
        //  { Key:8, Value:""          }
        // ]
    
        FString Val7 = FruitMap.FindRef(7);
        FString Val6 = FruitMap.FindRef(6);
        // Val7     == "Pineapple"
        // Val6     == ""
        // FruitMap == [
        //  { Key:5, Value:"Mango"     },
        //  { Key:2, Value:"Pear"      },
        //  { Key:7, Value:"Pineapple" },
        //  { Key:4, Value:"Kiwi"      },
        //  { Key:3, Value:"Orange"    },
        //  { Key:9, Value:"Melon"     },
        //  { Key:8, Value:""          }
        // ]
    
  • FindKeyFind相对,其返回指向与所提供值配对的第一个键的指针,搜索映射中不存在的值将返回nullptr

    • 如果映射有多个具有相同值的键
    • FindKey 可返回其中任一键
    const int32* KeyMangoPtr   = FruitMap.FindKey(TEXT("Mango"));   // *KeyMangoPtr   == 5
    const int32* KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat")); //  KeyKumquatPtr == nullptr 
    
  • GenerateKeyArray 使用所有键副本来填充 TArray

  • GenerateValueArray 使用所有值的副本来填充 TArray

  • 在这两种情况下,都会在填充前清空所传递的数组,因此产生的元素数量始终等于映射中的元素数量

     TArray<int32>   FruitKeys;
        TArray<FString> FruitValues;
        FruitKeys.Add(999);
        FruitKeys.Add(123);
        FruitMap.GenerateKeyArray  (FruitKeys);
        FruitMap.GenerateValueArray(FruitValues);
        // FruitKeys   == [ 5,2,7,4,3,9,8 ]
        // FruitValues == [ "Mango","Pear","Pineapple","Kiwi","Orange",
        //                  "Melon","" ]
    

移除元素

  • Remove 函数移除指定键的元素
    • 返回值是被移除元素的数量
    • 如果映射不包含与键匹配的元素,则返回值可为零
       FruitMap.Remove(8);
        // FruitMap == [
        //  { Key:5, Value:"Mango"     },
        //  { Key:2, Value:"Pear"      },
        //  { Key:7, Value:"Pineapple" },
        //  { Key:4, Value:"Kiwi"      },
        //  { Key:3, Value:"Orange"    },
        //  { Key:9, Value:"Melon"     }
        // ]
    
  • FindAndRemoveChecked函数可用于从map移除元素并返回其值
    • 若键不存在,映射将调用 check(UE4中等同于 assert)
     FString Removed7 = FruitMap.FindAndRemoveChecked(7);
        // Removed7 == "Pineapple"
        // FruitMap == [
        //  { Key:5, Value:"Mango"  },
        //  { Key:2, Value:"Pear"   },
        //  { Key:4, Value:"Kiwi"   },
        //  { Key:3, Value:"Orange" },
        //  { Key:9, Value:"Melon"  }
        // ]
    
        FString Removed8 = FruitMap.FindAndRemoveChecked(8);
        // Assert!
    
  • RemoveAndCopyValue的作用 与 Remove 相似,不同点是会将已移除元素的值复制到引用参数
    • 如果映射中不存在指定的键,则输出参数将保持不变,函数将返回 false
    FString Removed;
        bool bFound2 = FruitMap.RemoveAndCopyValue(2, Removed);
        // bFound2  == true
        // Removed  == "Pear"
        // FruitMap == [
        //  { Key:5, Value:"Mango"  },
        //  { Key:4, Value:"Kiwi"   },
        //  { Key:3, Value:"Orange" },
        //  { Key:9, Value:"Melon"  }
        // ]
        bool bFound8 = FruitMap.RemoveAndCopyValue(8, Removed);
        // bFound8  == false
        // Removed  == "Pear", i.e. unchanged
        // FruitMap == [
        //  { Key:5, Value:"Mango"  },
        //  { Key:4, Value:"Kiwi"   },
        //  { Key:3, Value:"Orange" },
        //  { Key:9, Value:"Melon"  }
        // ]
    
  • EmptyReset 函数可将映射中的所有元素移除
    • Empty 可采用参数指示映射中保留的slack量
    • Reset 则是尽可能多地留出slack量
    TMap<int32, FString> FruitMapCopy = FruitMap;
        // FruitMapCopy == [
        //  { Key:5, Value:"Mango"  },
        //  { Key:4, Value:"Kiwi"   },
        //  { Key:3, Value:"Orange" },
        //  { Key:9, Value:"Melon"  }
        // ]
    
        FruitMapCopy.Empty();       // We could also have called Reset() here.
        // FruitMapCopy == []
    

排序

  • KeySortValueSort 函数可分别按键和值进行排序,两个函数均使用二元谓词来进行排序
     FruitMap.KeySort([](int32 A, int32 B) {
            return A > B; // sort keys in reverse
        });
        // FruitMap == [
        //  { Key:9, Value:"Melon"  },
        //  { Key:5, Value:"Mango"  },
        //  { Key:4, Value:"Kiwi"   },
        //  { Key:3, Value:"Orange" }
        // ]
    
     FruitMap.ValueSort([](const FString& A, const FString& B) {
            return A.Len() < B.Len(); // sort strings by length
        });
        // FruitMap == [
        //  { Key:4, Value:"Kiwi"   },
        //  { Key:5, Value:"Mango"  },
        //  { Key:9, Value:"Melon"  },
        //  { Key:3, Value:"Orange" }
        // ]
    

运算符

  • 通过标准复制构造函数或赋值运算符进行复制,因为映射严格拥有其元素,复制映射的操作是深层的,所以新的映射将拥有其自己的元素副本
     TMap<int32, FString> NewMap = FruitMap;
        NewMap[5] = "Apple";
        NewMap.Remove(3);
        // FruitMap == [
        //  { Key:4, Value:"Kiwi"   },
        //  { Key:5, Value:"Mango"  },
        //  { Key:9, Value:"Melon"  },
        //  { Key:3, Value:"Orange" }
        // ]
        // NewMap == [
        //  { Key:4, Value:"Kiwi"  },
        //  { Key:5, Value:"Apple" },
        //  { Key:9, Value:"Melon" }
        // ]
    
  • MoveTemp函数可调用移动语义
  FruitMap = MoveTemp(NewMap);
    // FruitMap == [
    //  { Key:4, Value:"Kiwi"  },
    //  { Key:5, Value:"Apple" },
    //  { Key:9, Value:"Melon" }
    // ]
    // NewMap == []

Slack

  • Slack是不包含元素的已分配内存
  • Reserve可分配内存,无需添加元素
  • 通过非零slack参数调用 ResetEmpty 可移除元素,无需将其使用的内存取消分配
  • Slack优化了将新元素添加到映射的过程,因为可以使用预先分配的内存,而不必分配新内存
  • 它在移除元素时也十分实用,因为系统不需要将内存取消分配
  • 在清空希望用相同或更少的元素立即重新填充的映射时,此方法尤其有效
     FruitMap.Reserve(10);
        for (int32 i = 0; i < 10; ++i)
        {
            FruitMap.Add(i, FString::Printf(TEXT("Fruit%d"), i));
        }
        // FruitMap == [
        //  { Key:9, Value:"Fruit9" },
        //  { Key:8, Value:"Fruit8" },
        //  ...
        //  { Key:1, Value:"Fruit1" },
        //  { Key:0, Value:"Fruit0" }
        // ]
    
  • 使用 CollapseShrink 函数可移除 TMap 中的全部slack
    • Shrink 将从容器的末端移除所有slack,但这会在中间或开始处留下空白元素
     for (int32 i = 0; i < 10; i += 2)
        {
            FruitMap.Remove(i);
        }
        // FruitMap == [
        //  { Key:9, Value:"Fruit9" },
        //  <invalid>,
        //  { Key:7, Value:"Fruit7" },
        //  <invalid>,
        //  { Key:5, Value:"Fruit5" },
        //  <invalid>,
        //  { Key:3, Value:"Fruit3" },
        //  <invalid>,
        //  { Key:1, Value:"Fruit1" },
        //  <invalid>
        // ]
        FruitMap.Shrink();
        // FruitMap == [
        //  { Key:9, Value:"Fruit9" },
        //  <invalid>,
        //  { Key:7, Value:"Fruit7" },
        //  <invalid>,
        //  { Key:5, Value:"Fruit5" },
        //  <invalid>,
        //  { Key:3, Value:"Fruit3" },
        //  <invalid>,
        //  { Key:1, Value:"Fruit1" }
        // ]
    
    • 在上述代码中,Shrink 只删除了一个无效元素,因为末端只有一个空元素
    • 要移除所有slack,首先应调用 Compact 函数,将空白空间组合在一起,为调用 Shrink 做好准备
    FruitMap.Compact();
        // FruitMap == [
        //  { Key:9, Value:"Fruit9" },
        //  { Key:7, Value:"Fruit7" },
        //  { Key:5, Value:"Fruit5" },
        //  { Key:3, Value:"Fruit3" },
        //  { Key:1, Value:"Fruit1" },
        //  <invalid>,
        //  <invalid>,
        //  <invalid>,
        //  <invalid>
        // ]
        FruitMap.Shrink();
        // FruitMap == [
        //  { Key:9, Value:"Fruit9" },
        //  { Key:7, Value:"Fruit7" },
        //  { Key:5, Value:"Fruit5" },
        //  { Key:3, Value:"Fruit3" },
        //  { Key:1, Value:"Fruit1" }
        // ]
    
posted @ 2023-06-22 19:39  scyrc  阅读(729)  评论(0编辑  收藏  举报