C++映射擦除(开始,结束)不起作用

C++ map erase(begin, end) not working

本文关键字:结束 不起作用 开始 映射 擦除 C++      更新时间:2023-10-16

我正试图通过使用C++API for map来简化我的生活。通过map.erase(begin, end)方法,我希望删除[begin, end)之间的所有条目。所以我的方法实现了CCD_ 3的定义如下。

 79 void
 80 ObjectFinder::flush(uint64_t tableId) {
 81 
 82     RAMCLOUD_TEST_LOG("flushing object map");
 83     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::iterator lower;
 84     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::iterator upper;
 85     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::iterator it;
 86     KeyHash keyHash = Key::getHash(tableId, "", 0);
 87     TabletKey key(tableId, keyHash);
 88 
 89     std::cout << "before the loop" << std::endl;
 90     for (it = tableMap.begin(); it != tableMap.end(); it++) {
 91         std::cout << it->first.first << std::endl;
 92     }
 93     lower = tableMap.lower_bound(key);
 94     upper = tableMap.upper_bound(key);
 95     
108     tableMap.erase(lower, upper);
109     std::cout << "After the erase" << std::endl;
110     for (it = tableMap.begin(); it != tableMap.end(); it++) {
111         std::cout << it->first.first << std::endl;
112     }
    }

但是,id值没有被删除:

id = 99
before the loop
1
99
After the erase
1
99

我编写了自己的comparison函数,以重载默认方法:

 35 typedef std::pair<uint64_t, KeyHash> TabletKey;
 36 
 37 /*
 38  * The object CmpTabletKey is used to override the default comparison 
 39  * definition from the C++ Map.
 40  */
 41 struct CmpTabletKey {
 42     bool operator()(const TabletKey& key1, const TabletKey& key2) const {
 43         return ((key1.first < key2.first) ||
 44                 (key1.first == key2.first && key1.second < key2.second));
        }
    }

有人能告诉我为什么erase没有按预期工作吗?我是否也必须将CmpTabletKey的定义赋予iterator更新这是我以前的实现:它工作得很好,可以做我想做的事:然而,这是一个O(n)方法,我想要一个更快的实现:

117     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::iterator it;
118     for (it = tableMap.begin(); it != tableMap.end(); ) {
119         if (tableId == it->first.first) {
120             tableMap.erase((it++)->first);
121         } else {
122             ++it;
123         }
124     }

从迭代器的定义来看,您似乎没有使用自定义比较器创建映射。

我认为地图应该创建为:

std::map<TabletKey, ProtoBuf::Tablets::Tablet, CmpTabletKey > tableMap; //CmpTabletKey is passed as the comparator type
And your iterators become: 
std::map<TabletKey, ProtoBuf::Tablets::Tablet, CmpTabletKey>::iterator lower;
std::map<TabletKey, ProtoBuf::Tablets::Tablet, CmpTabletKey>::iterator upper;
std::map<TabletKey, ProtoBuf::Tablets::Tablet, CmpTabletKey>::iterator it;

如果未指定CmpTabletKey作为类型,则映射将使用默认比较器。

映射容器最多包含每个键的一个副本,我认为您想要做的是擦除TableID相同的所有元素,但您已经使用一对对元素进行了排序,因此您必须迭代所有映射并选择符合谓词的元素。

如果可以的话,应该使用std::multimap并创建一个只涉及TableID的比较器。

编辑:

好吧,所以你想删除所有具有特定ID的元素。但实际上,你的地图正在寻找的是一个具有特定tableID和特定keyHash的元素。

您有几种解决方案,其中一种是O(n)(您现有的解决方案),另一种选择是使用另一种满足您需求的数据结构。我认为您应该使用多映射或未定义的多映射(hast_table)。

这里有一个简化的示例,可以帮助您实现目标。

让我们用一对钥匙做一张地图:

#include <map>
#include <utility>
typedef std::pair<int, int> key_type;
std::map<key_type, void *> mymap = { { { 1, 2}, NULL }
                                   , { { 5, 0}, NULL }
                                   , { { 5, 7}, NULL }
                                   , { { 6, 3}, &mymap } };

现在假设我们想要从mymap中移除其第一个关键部分等于5的所有元素。这里有一个解决方案:

for (auto it = mymap.begin(); it != mymap.end(); )
{
    if (it->first.first == 5) { mymap.erase(it++); }
    else                      { ++it;              }
}

取2…由于您的原始文件甚至没有考虑key.second值,我猜问题出在CmpTabletKey中,但这次原因不同。与原始实现功能等效的比较运算符是:

 35 typedef std::pair<uint64_t, KeyHash> TabletKey;
 36 
 37 /*
 38  * The object CmpTabletKey is used to override the default comparison 
 39  * definition from the C++ Map.
 40  */
 41 struct CmpTabletKey {
 42     bool operator()(const TabletKey& key1, const TabletKey& key2) const {
 43         return (key1.first < key2.first);
        }
    }

我怀疑,由于您当前的实现也考虑了.second值,因此功能与原来的有所不同。擦除同一个对象的下界和上界之间的所有内容本质上会擦除与该对象等价的所有键,但是您有KeyHash keyHash = Key::getHash(tableId, "", 0);TabletKey key(tableId, keyHash);,但key的键值并没有具体在您的映射中。您的比较运算符也是特定于的,因此它不会仅评估.first值的等价性。

同时,比较有必要如此严格,因为我相信地图搜索基本上是说"如果A!<B和B!<A,那么A==B"。这意味着您将无法正确插入值,因为等价性也不关心.second。由于这些相互冲突的定义,我认为您当前的实现不会起作用。

编辑:Derp。试试这个:

 79 void
 80 ObjectFinder::flush(uint64_t tableId) {
 81 
 82     RAMCLOUD_TEST_LOG("flushing object map");
 83     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::iterator lower;
 84     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::iterator upper;
 85     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::iterator it;
 86     //KeyHash keyHash = Key::getHash(tableId, "", 0);
        TabletKey keylower(tableId, KeyHash::MINIMUM);
        TabletKey keyupper(tableId, KeyHash::MAXIMUM);
 87     //TabletKey key(tableId, keyHash);
 88 
 89     std::cout << "before the loop" << std::endl;
 90     for (it = tableMap.begin(); it != tableMap.end(); it++) {
 91         std::cout << it->first.first << std::endl;
 92     }
 93     lower = tableMap.lower_bound(keylower);
 94     upper = tableMap.upper_bound(keyupper);
 95     
108     tableMap.erase(lower, upper);
109     std::cout << "After the erase" << std::endl;
110     for (it = tableMap.begin(); it != tableMap.end(); it++) {
111         std::cout << it->first.first << std::endl;
112     }
    }

其中,KeyHash::MINIMUM是最低密钥哈希值的静态常量,KeyHash::MAXIMUM是最大值。请使用当前的比较结构。