C++ 中的嵌套 unordered_map / hash_map 更新
Nested unordered_map / hash_map update in C++
我是 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;
}
输出很奇怪。对于 h1 和 h2 它似乎有效,这意味着 h1 中键为 0 的值已更新(增加 1)。虽然这看起来微不足道,但对于 h3,我随机插入一些 "pairs"(第一,第二)并使用哈希映射计数,计数似乎无法更新。例如,它可能是这样的:
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!
说明值更新不成功。我知道在 Java 这永远不会发生。那么这个问题出在哪里呢?
这是代码第二部分的重构版本(我认为)。我还生成了一个一致的测试数据集,因此我们可以在每个 运行 上重现行为(随机性是测试的诅咒)。
这是代码。问题是什么?
#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;
}
问题在这里:
unordered_map<int,int> submap = h3_itor->second;
这导致整个子图被复制到你新的本地submap
对象中。当它在离开范围时被销毁时,您对其所做的所有修改都会丢失。
相反,您可以使用对要修改的实际 hashmap 元素的引用:
unordered_map<int,int> &submap = h3_itor->second;
这个 &
应该可以解决所有问题。
不是真正的答案,仅供参考:如果这不仅仅是使用迭代器的练习,还有更简单的方法:
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]++;
}
这是有效的,因为如果缺少一个值,unordered_map::operator[]
将默认构建并插入它。对于 map,这是一个空映射,对于 int,它是零。
如果你想要另一个默认值,你可以使用 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;
}
是的,这有点让人困惑:emplace
如果键丢失则插入指定的值(如果它已经存在则不会覆盖),returns std::pair<iterator, bool>
。
在这里,bool 告诉你你的值是否被插入,迭代器本身是 std::pair<key,val>*
的包装器,因此 .first->second
得到值。
除了 shorter/more 可读性之外,这两者也更有效。在您的代码中,如果该值不存在,您将进行两次查找,但以上两者都只进行一次查找。
我是 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;
}
输出很奇怪。对于 h1 和 h2 它似乎有效,这意味着 h1 中键为 0 的值已更新(增加 1)。虽然这看起来微不足道,但对于 h3,我随机插入一些 "pairs"(第一,第二)并使用哈希映射计数,计数似乎无法更新。例如,它可能是这样的:
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!
说明值更新不成功。我知道在 Java 这永远不会发生。那么这个问题出在哪里呢?
这是代码第二部分的重构版本(我认为)。我还生成了一个一致的测试数据集,因此我们可以在每个 运行 上重现行为(随机性是测试的诅咒)。
这是代码。问题是什么?
#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;
}
问题在这里:
unordered_map<int,int> submap = h3_itor->second;
这导致整个子图被复制到你新的本地submap
对象中。当它在离开范围时被销毁时,您对其所做的所有修改都会丢失。
相反,您可以使用对要修改的实际 hashmap 元素的引用:
unordered_map<int,int> &submap = h3_itor->second;
这个 &
应该可以解决所有问题。
不是真正的答案,仅供参考:如果这不仅仅是使用迭代器的练习,还有更简单的方法:
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]++;
}
这是有效的,因为如果缺少一个值,unordered_map::operator[]
将默认构建并插入它。对于 map,这是一个空映射,对于 int,它是零。
如果你想要另一个默认值,你可以使用 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;
}
是的,这有点让人困惑:emplace
如果键丢失则插入指定的值(如果它已经存在则不会覆盖),returns std::pair<iterator, bool>
。
在这里,bool 告诉你你的值是否被插入,迭代器本身是 std::pair<key,val>*
的包装器,因此 .first->second
得到值。
除了 shorter/more 可读性之外,这两者也更有效。在您的代码中,如果该值不存在,您将进行两次查找,但以上两者都只进行一次查找。