将std::map数据复制到另一个map

31

我有一个这样定义的映射表

 struct A
 {
  int A;
  int B;
 };
 typedef map<int,A> Amap;

我有一个Amap1,我想要复制它到Amap2

 A a....;
 Amap Amap1,Amap2;
 Amap1[1]=a1;
 Amap1[2]=a2;
 Amap1[3]=a3;
 Amap2.insert(Amap1.begin(), Amap1.end());
有时这个函数能正常工作,有时它只复制键和值为0的内容。我在哪里出错了?
```

有时这个函数能正常工作,有时它只复制键和值为0的内容。我在哪里出错了?

```
3个回答

74

可以使用赋值运算符或拷贝构造函数将一个映射复制到另一个映射中。

例如:

map<X, Y> mp1; 
//fill mp1 with data
map<X, Y> mp2(mp1); //mp2 is a copy of mp1 (via copy-construction)
map<X, Y> mp3;
mp3 = mp2; // mp3 is also a copy of mp2 (via copy-assignment)

@Wolf:请不要大幅度修改其他人的答案,尤其是那些已经被人赞同并被提问者接受的五年前写的答案。你可以自由地撰写自己的、竞争性的答案。 - Lightness Races in Orbit
1
@LightnessRacesinOrbit 我请求了它,而且 OP 明确地授予了我:*随意编辑*。 - Wolf
@LightnessRacesinOrbit 我明白了,谢谢你的解释。但是,如果回答的作者在5年后决定对其进行改进怎么办? - Wolf
2
@Wolf: 这是一个有争议的问题。在Meta上这个问题被问得很多,我认为没有达成任何共识。个人而言,如果需要做出实质性的修改,我认为作者应该直接发布全新的回答;无论出于何种良好意图,无效化所有已存在的投票似乎是不诚实的行为。毕竟,这些投票是用来对原始内容/建议进行同行评审的。 - Lightness Races in Orbit
1
赋值操作是否总是会拷贝?如果使用std::move呢? - David Callanan
显示剩余4条评论

18

假设Amap2是空的,那么您上面发布的代码将正确工作。如果您尝试向已经包含该键的mapinsert一个键值对,则旧值将被保留且新值将被丢弃。因此,如果您编写以下内容:

Amap2.insert(Amap1.begin(), Amap1.end());
在某些情况下,由于重复键不会被复制,您可能无法像预期的那样完全复制所有内容。
要将Amap2设置为等于Amap1,请考虑仅使用赋值运算符:
Amap2 = Amap1;

然而,这样做会盲目地丢弃Amap2的内容,因此在执行此操作时要小心。

如果你想要将Amap2中所有的键值对完全覆盖到Amap1中,可以使用以下逻辑。 这里的思想类似于归并排序 - 我们将映射视为已排序值的序列,然后不断地将两个序列合并在一起:

void MergeMaps(map<int, A>& lhs, const map<int, A>& rhs) {
    map<int, A>::iterator lhsItr = lhs.begin();
    map<int, A>::const_iterator rhsItr = rhs.begin();

    while (lhsItr != lhs.end() && rhsItr != rhs.end()) {
        /* If the rhs value is less than the lhs value, then insert it into the 
           lhs map and skip past it. */
        if (rhsItr->first < lhsItr->first) {
            lhs.insert(lhsItr, *rhsItr); // Use lhsItr as a hint.
            ++rhsItr;
        }
        /* Otherwise, if the values are equal, overwrite the lhs value and move both
           iterators forward. */
        else if (rhsItr->first == lhsItr->first) {
            lhsItr->second = rhsItr->second;
            ++lhsItr; ++rhsItr;
        }
        /* Otherwise the rhs value is bigger, so skip past the lhs value. */
        else
            ++lhsItr;

    }

    /* At this point we've exhausted one of the two ranges.  Add what's left of the
       rhs values to the lhs map, since we know there are no duplicates there. */
    lhs.insert(rhsItr, rhs.end());
}

通过这个,你可以编写

MergeMaps(Amap1, Amap2);

将所有Amap2中的键值对复制到Amap1中。

希望这能有所帮助!


2
如果我想确保现有的键被覆盖,我可能会选择两个选项:我要么使用std::copy,但是不使用std::inserter,而是编写一个自定义的插入器,使用operator[]。另一种选择是将rhsItr复制,然后将lhsItr常规插入到副本中,最后交换。这种方法可能不太高效,但只需要两行代码。 - CashCow
1
@CashCow:与自定义插入相比,我的方法的主要优势在于括号的平均情况下运行时间为O(lg n),而我发布的代码应该在线性时间内运行,假设插入的实现良好,因为具有附近迭代器的提示。 - templatetypedef

0
现在最简单的答案,如果你不反对使用auto,就是这样:
Amap Amap1;
Amap1[1] = a1;
Amap1[2] = a2;
Amap1[3] = a3;
auto Amap2 = Amap1;

那是复制构造函数,正如另一个答案中所指出的,但不需要声明Amap2的类型,因为楼主想要一个完全相同的副本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接