简体   繁体   English

在C ++中嵌套的unordered_map / hash_map更新

[英]Nested unordered_map / hash_map update in C++

I'm new in C++ and have the following problems about unordered_map (or hash_map): 我是C ++的新手,关于unordered_map(或hash_map)存在以下问题:

#include <unordered_map>
#include <iostream>
using namespace std;

int main()
{
    unordered_map<int,int> h1;
    int temp1=0;
    h1.insert(pair<int,int>(0,temp1));
    unordered_map<int, unordered_map<int,int>> h2;
    h2.insert(pair<int, unordered_map<int,int>>(1,h1));
    unordered_map<int, unordered_map<int,int>>::iterator h2_itor=h2.find(1);
    h2_itor->second.find(0)->second++;

    unordered_map<int, unordered_map<int,int>> h3;
    for(int i=0;i<100;i++)
    {
        int first=rand()%10;
        int second=rand()%10;

        unordered_map<int, unordered_map<int,int>>::iterator h3_itor=h3.find(first);
        if(h3_itor!=h3.end())
        {
            unordered_map<int,int> submap=h3_itor->second;
            unordered_map<int,int>::iterator submap_itor=submap.find(second);
            if(submap_itor!=submap.end())
                submap_itor->second++;
            else
                submap.insert(pair<int,int>(second, 1));
        }
        else
        {
            unordered_map<int,int> submap;
            submap.insert(pair<int,int>(second,1));
            h3.insert(pair<int, unordered_map<int,int>>(first,submap));
        }
    }

    return 0;
}

The output is quite odd. 输出是很奇怪的。 For h1 and h2 it seems work, which means the value in h1 with key 0 is updated (incremented by 1). 对于h1和h2来说似乎可行,这意味着用键0更新了h1中的值(增加了1)。 Though this looks trivial, for h3, which I randomly insert some "pairs" (first, second) and counting with hash map, the count seems can not be updated. 尽管这看起来很琐碎,但对于h3,我随机插入了一些“对”(第一对,第二对)并使用哈希映射进行计数,但该计数似乎无法更新。 For example, it may be like this: 例如,可能是这样的:

insert 1 -> 7 -> 1 
 ... 
now update 1 -> 7 -> 1 to 1 -> 7 -> 2 using my code
fetch: h3.find(1)->second.find(7)->second : it's still 1 but not 2!

which shows the update of value is unsuccessful. 这表明更新值不成功。 I know in Java this never happens. 我知道在Java中这永远不会发生。 So where does this problem lies? 那么这个问题在哪里呢?

The problem is here: 问题在这里:

unordered_map<int,int> submap = h3_itor->second;

This results in the whole submap being copied into you new local submap objects. 这样会导致整个子图被复制到新的本地submap对象中。 All the modifications you do to it are lost when it gets destroyed upon leaving the scope. 当您离开示波器时销毁它时,您对其所做的所有修改都会丢失。

Instead you can use the reference to the actual hashmap element you want to modify: 相反,您可以使用对要修改的实际hashmap元素的引用:

unordered_map<int,int> &submap = h3_itor->second;

This one & should fix everything. 这个&应该解决所有问题。

Here's a refactored version (i think) of the second part of the code. 这是代码第二部分的重构版本(我认为)。 I am also generating a consistent test data set so we can reproduce behaviour on each run (randomness is an anathema to testing). 我还在生成一致的测试数据集,以便我们可以在每次运行时重现行为(随机性是测试的厌恶之物)。

Here's the code. 这是代码。 What's the question? 问题是什么?

#include <unordered_map>
#include <iostream>

using i2i = std::unordered_map<int, int>;
using map_i_to_i2i = std::unordered_map<int, i2i>;

void test_insert(map_i_to_i2i& outer, int v1, int v2)
{
    auto iouter = outer.find(v1);
    if (iouter == outer.end()) {
        std::cout << "case c) [" << v1 << "] = [" << v2 << "] = 1\n";
        outer[v1][v2] = 1;
    }
    else {
        auto& inner = iouter->second;
        auto iinner = inner.find(v2);
        if (iinner == inner.end())
        {
            std::cout << "case b) [" << v1 << "][" << v2 << "] = 1\n";
            inner.emplace_hint(iinner, v2, 1);
        }
        else {
            std::cout << "case c) [" << v1 << "][" << v2 << "] += 1\n";
            iinner->second += 1;
        }
    }
}

int main()
{
    map_i_to_i2i h3;
    for (int passes = 0 ; passes < 3 ; ++passes)
    {
        for (int x = 0 ; x < 2 ; ++x) {
            for (int y = 0 ; y < 2 ; ++y) {
                test_insert(h3, x, y);
            }
        }
    }
    return 0;
}

Not really an answer, just an FYI: if this is more than an exercise in using iterators, there is a much easier way to do this: 并不是真正的答案,只是一个供参考:如果这不仅仅是使用迭代器的练习,那么还有一种更简单的方法:

unordered_map<int, unordered_map<int,int>> h3
h3[0][0] = 1;

for (int i=0; i<100; i++ ) {
    int first=rand()%10;
    int second=rand()%10;
    h3[first][second]++;
}

This works because, if a value is missing, unordered_map::operator[] will default construct and insert it. 之所以可行,是因为如果缺少一个值, unordered_map::operator[]将默认构造并插入它。 For a map, that is an empty map, and for an int, that's zero. 对于地图,这是一个空地图,对于整数,它是零。

If you wanted another default, you could use unordered_map::emplace , eg: 如果您想要另一个默认值,则可以使用unordered_map::emplace ,例如:

unordered_map<int, unordered_map<int,int>> h3
h3[0][0] = 2;

for (int i=0; i<100; i++ ) {
    int x=rand()%10;
    int y=rand()%10;
    int& val = h3[x].emplace(y, 1).first->second;
    val *= 2;
}

Yes, thats slightly more confusing: emplace inserts the specified value if the key is missing (no overwriting if it already exists), and returns std::pair<iterator, bool> . 是的,那有点令人困惑:如果缺少键,则emplace插入指定的值(如果已经存在,则不会覆盖),然后返回std::pair<iterator, bool>

Here, the bool tells you if your value was inserted or not, and iterator itself is a wrapper around std::pair<key,val>* , hence the .first->second to get the value out. 在这里,布尔值会告诉您是否插入了值,并且迭代器本身是std::pair<key,val>*的包装,因此使用.first->second值。

Besides being shorter/more readable, both of these are also more efficient. 除了更短/更易读之外,这两种方式都更加有效。 In your code, you do a lookup twice if the value isn't present, but both of the above only do one lookup. 在您的代码中,如果不存在该值,您将进行两次查找,但是以上两种方法均只进行一次查找。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM