我理解为什么人们不能这样做(重新平衡和填充):
iterator i = m.find(33);
if (i != m.end())
i->first = 22;
但到目前为止,改变密钥的唯一方法(我知道)是从树中删除节点,然后用不同的密钥插回值:
iterator i = m.find(33);
if (i != m.end())
{
value = i->second;
m.erase(i);
m[22] = value;
}
由于更多原因,这似乎对我来说效率低下:
我发现分配和释放是这三者中最差的。我错过了什么或有更有效的方法吗?
更新: 我认为,从理论上讲,它应该是可能的,所以我不认为改变不同的数据结构是合理的。这是我想到的伪算法:
答案 0 :(得分:31)
在C ++ 17中,新的map::extract
功能可让您更改密钥
例如:
std::map<int, std::string> m{ {10, "potato"}, {1, "banana"} };
auto nodeHandler = m.extract(10);
nodeHandler.key() = 2;
m.insert(std::move(nodeHandler)); // { { 1, "banana" }, { 2, "potato" } }
答案 1 :(得分:30)
我在18个月前提出了关联容器的算法:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#839
寻找标记的评论:[2009-09-19 Howard补充说:]。
当时,我们太过接近FDIS来考虑这种变化。但是我认为它非常有用(你显然同意),我想把它送到TR2。也许您可以通过查找并通知您的C ++ National Body代表,这是您希望看到的功能。
<强>更新强>
不确定,但我认为我们很有可能会在C ++ 17中看到这个功能! : - )
答案 2 :(得分:23)
您可以省略复制值;
const int oldKey = 33;
const int newKey = 22;
const iterator it = m.find(oldKey);
if (it != m.end()) {
// Swap value from oldKey to newKey, note that a default constructed value
// is created by operator[] if 'm' does not contain newKey.
std::swap(m[newKey], it->second);
// Erase old key-value from map
m.erase(it);
}
答案 3 :(得分:6)
STL映射中的键必须是不可变的。
似乎如果你在配对的关键方面有那么大的波动性,可能会有更多不同的数据结构或结构。
答案 4 :(得分:5)
你不能。
正如您所注意到的那样,这是不可能的。组织地图以便您可以有效地更改与密钥关联的值,但不能相反。
你看看Boost.MultiIndex,特别是它的Emulating Standard Container sections。 Boost.MultiIndex容器具有高效的更新功能。
答案 5 :(得分:1)
您应该将分配留给分配器。 : - )
正如你所说,当密钥发生变化时,可能会有很多重新平衡。这就是树的运作方式。也许22是树中的第一个节点,33是最后一个节点?我们知道什么?
如果避免分配很重要,也许您应该尝试使用矢量或双端队列?它们分配更大的块,因此它们节省了对分配器的调用次数,但可能会浪费内存。所有的容器都有它们的权衡,由你来决定哪种容器在每种情况下都具有你需要的主要优势(假设它很重要)。
对于喜欢冒险的人:
如果您确定更改密钥不会影响订单,并且您永远不会犯错,那么一点const_cast 会让您更改密钥。
答案 6 :(得分:0)
如果您知道新键对于地图位置有效(更改它不会更改顺序),并且您不想执行将项目添加和添加到地图的额外工作,则可以使用const_cast
来更改密钥,例如下面的unsafeUpdateMapKeyInPlace
:
template <typename K, typename V, typename It>
bool isMapPositionValidForKey (const std::map<K, V>& m, It it, K key)
{
if (it != m.begin() && std::prev (it)->first >= key)
return false;
++it;
return it == m.end() || it->first > key;
}
// Only for use when the key update doesn't change the map ordering
// (it is still greater than the previous key and lower than the next key).
template <typename K, typename V>
void unsafeUpdateMapKeyInPlace (const std::map<K, V>& m, typename std::map<K, V>::iterator& it, K newKey)
{
assert (isMapPositionValidForKey (m, it, newKey));
const_cast<K&> (it->first) = newKey;
}
如果您想要一个仅在有效时就地更改的解决方案,否则会更改地图结构:
template <typename K, typename V>
void updateMapKey (const std::map<K, V>& m, typename std::map<K, V>::iterator& it, K newKey)
{
if (isMapPositionValidForKey (m, it, newKey))
{
unsafeUpdateMapKeyInPlace (m, it, newKey);
return;
}
auto next = std::next (it);
auto node = m.extract (it);
node.key() = newKey;
m.insert (next, std::move (node));
}