【问题标题】:C++: insert into std::map without knowing a keyC++:在不知道密钥的情况下插入 std::map
【发布时间】:2011-09-22 09:49:56
【问题描述】:

我需要将值插入到任何空闲位置的 std::map (或等效的)中,然后获取它的键(稍后删除/修改)。比如:

std::map<int, std::string> myMap;
const int key = myMap.insert("hello");

是否可以使用 std::map 这样做,或者是否有一些合适的容器?

谢谢。

【问题讨论】:

  • 我不认为 std::map 是您想要实现的目标的正确选择。你想做什么?将没有关联键的对象添加到字典中是没有意义的。
  • 这不是它的工作原理...map,通常被实现为 RB 树,没有与任何空闲槽关联的“默认密钥”。你到底想达到什么目的?
  • 可能您正在寻找哈希函数 - 搜索有关此主题的信息。
  • @Matteo Italia,我需要经常在任何数组中插入/更新/删除对象的方法。我认为 std::map 的(有序树?)实现可以找到一些未占用的键 - 不是吗?
  • @Slav:不,它不是这样工作的。地图中没有“未占用的键”。当地图没有某个键的数据时,它也没有该键本身的数据。当您有一个空的 map&lt;uint32_t&gt; 时,您有超过 40 亿个“未占用的键”...

标签: c++ insert key stdmap


【解决方案1】:

除了使用set,您还可以保留已分配(或空闲)的列表 密钥,并在插入之前找到一个新密钥。对于由索引的map int,你可以简单地取最后一个元素,并增加它的键。但 我宁愿选择一个简单的std::vector;如果删除不是 支持,你可以做一些简单的事情,比如:

int key = myVector.size();
myVector.push_back( newEntry );

如果你需要支持删除,那么使用某种向量 “也许”类型(boost::optional,等等——你可能已经有了 一个在你的工具箱中,可能以 Fallible 或 Maybe 的名义)可能是 合适的。取决于使用模式(删除的数量与 总条目等),您可能需要搜索向量以便 重用条目。如果你真的有野心,你可以保留一张位图 免费条目,每次删除和条目时设置一点,以及 每当您重复使用空间时重置它。

【讨论】:

  • “您可能想要搜索向量以重用条目” - 或保留一堆免费索引。删除条目时推送堆栈,添加新条目时堆栈为空(在这种情况下push_back 上述向量),或者使用从堆栈中弹出的索引。
  • 保留一堆未使用的条目是一个非常好的主意。它实现起来非常简单,在运行时方面也很高效。
  • 还有一个可能并不明显的性能优势,即以 LIFO 顺序使用旧条目有助于保持缓存温暖。
  • @Steve Jessop 也许吧。如果您找到一个范围并在该范围内分配,则最近分配的值将具有良好的局部性。当然,如果有很多空槽,使用位图可能会导致使用的内存显着减少(这也有助于局部性)。 (预先确定最适合本地化的方法通常是一场真正的猜谜游戏。)
【解决方案2】:

您可以将对象添加到 std::set,然后将整个集合放入映射中。但是不,你不能在没有键的情况下将值放入映射中。

【讨论】:

    【解决方案3】:

    与您尝试做的最接近的事情可能是

    myMap[myMap.size()] = "some string";
    

    std::set 相比,唯一的优势是您可以将整数索引传递给其他模块,而无需知道std::set&lt;Foo&gt;::iterator 或类似的类型。

    【讨论】:

    • 这是一个聪明的把戏。唯一的问题是是否有删除;你可能会得到一个已经使用过的索引。像prec(myMap.end())-&gt;first(假设地图不是空的)这样的东西可以解决这个问题(当然,如果应用程序不需要删除,你的技巧就可以开箱即用)。
    【解决方案4】:

    这是不可能的。这样的操作将需要对密钥类型的复杂知识才能知道哪些密钥可用。例如,std::map 必须为 int 映射增加 int 值,或者为字符串映射附加到字符串。

    您可以完全使用 std::set 和删除键控。

    【讨论】:

    • 一般来说你是对的,但在他的情况下,密钥类型是int,所以我们知道该怎么做。
    • 是的,但前提是您只需要存储小整数或在任何情况下都不需要密钥类型。假设您从外部来源获得了一些具有高整数的 ID,并且想要添加您自己的 ID(地图的典型用例),那么简单的向量方法将不起作用。我喜欢你的解决方案,但他低估了他的问题:-)
    • 如果某些 id 被强加(并且可能非常大),如您所说,std::vector 方法将不起作用。在这种情况下,您可以将最后一个元素的索引 + 1 用于新元素。或者您可以迭代以找到一个洞(可能将迭代的结果缓存为一个范围,因此您不必每次都进行迭代)。或者可能是别的什么——正如你所说,这个问题被严重低估了。
    【解决方案5】:

    如果您想要实现类似于 SQL 数据库中自动生成的主键的功能,那么您可以维护一个计数器并使用它来生成唯一键。但也许 std::set 才是你真正需要的。

    【讨论】:

      猜你喜欢
      • 2021-12-20
      • 2021-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多