合并两个STL贴图

Jon*_*onF 62 c++ maps merge stl stdmap

如何将两个STL地图合并为一个?它们都具有相同的键值类型(map).如果键有重叠,我想优先考虑其中一个地图.

jke*_*ian 117

假设您要保留元素mapA,并合并mapB没有键的元素mapA:

mapA.insert(mapB.begin(), mapB.end())
Run Code Online (Sandbox Code Playgroud)

我想会做你想做的事.

工作范例:

#include <iostream>
#include <map>

void printIt(std::map<int,int> m) {
    for(std::map<int,int>::iterator it=m.begin();it!=m.end();++it)
        std::cout << it->first<<":"<<it->second<<" ";
    std::cout << "\n";
}

int main() {
    std::map<int,int> foo,bar;
    foo[1] = 11; foo[2] = 12; foo[3] = 13;
    bar[2] = 20; bar[3] = 30; bar[4] = 40;
    printIt(foo);
    printIt(bar);
    foo.insert(bar.begin(),bar.end());
    printIt(foo);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

:!./insert
1:11 2:12 3:13
2:20 3:30 4:40
1:11 2:12 3:13 4:40
Run Code Online (Sandbox Code Playgroud)

  • Insert不会覆盖现有元素,当键中存在冲突时,现有元素优先. (13认同)
  • 这其中的复杂性是什么?是n log(n)吗,其中n是源映射中的元素数量。还是复杂度可以低一点(合并两棵红黑树)? (2认同)
  • 标准说复杂性是n log(n)(来源:cppreference) (2认同)
  • @galinette不是,它是O(n log(n + m)),其中n是源范围的大小(在这种情况下,实际上是源地图的大小),而m是目标地图的大小。(在特殊情况下,源范围按目标的“ value_comp()”排序,这可以实现为O(n(1 + log(m /(1 + n)))+ log(m)),但该标准并未强制要求这样做。) (2认同)

kiw*_*nga 30

如果要将条目从一个地图复制到另一个地图,可以使用std::map's insert:

targetMap.insert(sourceMap.begin(), sourceMap.end());
Run Code Online (Sandbox Code Playgroud)

但请注意,insert如果元素的键已经存在于targetMap中,则不会更新元素; 这些物品将保持原样.要覆盖元素,您必须明确复制,例如:

for(auto& it : sourceMap)
{
    targetMap[it.first] = it.second;
}
Run Code Online (Sandbox Code Playgroud)

如果你不介意丢失数据sourceMap,另一种实现复制和覆盖的方法是insert将目标转换为源和std::swap结果:

sourceMap.insert(targetMap.begin(), targetMap.end());
std::swap(sourceMap, targetMap);
Run Code Online (Sandbox Code Playgroud)

交换后,sourceMap将包含targetMap旧数据,并将targetMap合并两个地图,优先选择sourceMap条目.


hon*_*onk 13

C++17

正如John Perry's answer 中提到,因为C++17 std::map提供了一个merge()成员函数。该merge()函数为目标地图生成与基于 using 的jkerian 解决方案相同的结果insert(),正如您从以下示例中看到的,我从 jkerian 借用了该示例。我刚刚用一些C++11和 C++17 特性更新了代码(例如using类型别名、具有结构化绑定的基于范围的 for 循环列表初始化):

using mymap = std::map<int, int>;

void printIt(const mymap& m) {
    for (auto const &[k, v] : m)
        std::cout << k << ":" << v << " ";
    std::cout << std::endl;
}

int main() {
    mymap foo{ {1, 11}, {2, 12}, {3, 13} };
    mymap bar{ {2, 20}, {3, 30}, {4, 40} };
    printIt(foo);
    printIt(bar);
    foo.merge(bar);
    printIt(foo);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

1:11 2:12 3:13
2:20 3:30 4:40
1:11 2:12 3:13 4:40

如您所见,当键重叠时,merge()也会优先考虑目标地图foo。如果你想反过来,那么你必须调用bar.merge(foo);.

但是,使用insert()merge()关于源映射发生的事情之间存在差异。这些insert()函数向目标映射添加新条目,同时merge()从源映射移动条目。这意味着对于上面的示例, thatinsert()不会改变bar,而是从 中merge()删除,因此只有和保留在 中。4:40bar2:203:30bar

注意:map<int, int>为了简洁起见,我重用了 jkerian 中的示例,但merge()也适用于您的map<string, string>.

Coliru 上的代码


Joh*_*rry 12

请注意,从C++ 17开始,有一种merge()映射方法.