C++容器之无序映射(std::unordered_map)

1 概述

  无序映射是一种关联容器,用于存储由键值和映射值组合而成的元素,并允许基于其键快速检索单个元素。
  在无序映射中,键值通常用于唯一标识元素,而映射的值是一个对象,其内容与该键相关联。键和映射值的类型可能不同。
  在内部,无序映射中的元素不按任何特定的顺序相对于其键值或映射值进行排序,而是根据其哈希值组织到桶中,以允许直接通过其键值快速访问各个元素(平均具有恒定的平均时间复杂度)。
  无序映射容器比映射容器更快地通过关键字访问单个元素,尽管它们通常在通过元素子集进行范围迭代时效率较低。
无序映射实现了直接访问运算符(运算符[]),该运算符允许使用映射值的键值作为参数直接访问映射值。
容器中的迭代器至少是前向迭代器。
容器特性:

  • 关联性 关联容器中的元素由它们的键(Key)引用,而不是由它们在容器中的绝对位置引用。
  • 无序性 无序容器使用哈希表来组织其元素,哈希表允许通过其键快速访问元素。
  • 映射性 每个元素都将一个键与映射值相关联:键用于标识其内容为映射值的元素。
  • 唯一性 容器中没有两个元素可以具有等效的键。
  • 分配器感知 容器使用分配器对象来动态处理其存储需求。
  • 其类图如下:
    类图

2 使用实例

void UnorderedMapSuite::rehash()
{
    std::unordered_map<std::string, std::string> a;

    a.rehash(12);
    TEST_ASSERT_EQUALS(true, a.bucket_count() > 12)

    a.insert({ "apple", "red" });
    a.insert({ "pear", "yellow" });
    a.insert({ "banana", "yellow" });
    a.insert({ "banana", "yellow" });

    uint32_t bucket_count = a.bucket_count();
    a.rehash(11);
    TEST_ASSERT_EQUALS(bucket_count, a.bucket_count())

    a.rehash(5);
    TEST_ASSERT_EQUALS(true, a.bucket_count() < bucket_count)
}

3 接口使用

3.1 construct

template<class T>
T merge_map(T const & a, T const& b)
{
    T t(a);
    t.insert(b.begin(), b.end());
    return t;
}

void UnorderedMapSuite::construct()
{
    std::unordered_map<std::string, std::string> a;
    std::unordered_map<std::string, std::string> b({ { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } });
    std::unordered_map<std::string, std::string> c({ { "mango", "yellow" }, { "strawberry", "red" } });
    std::unordered_map<std::string, std::string> d(b);
    std::unordered_map<std::string, std::string> e(merge_map(b, c));
    std::unordered_map<std::string, std::string> f(e.begin(), e.end());

    TEST_ASSERT_EQUALS(true, a.empty())
    TEST_ASSERT_EQUALS(3, b.size())
    TEST_ASSERT_EQUALS(2, c.size())
    TEST_ASSERT_EQUALS(3, d.size())
    TEST_ASSERT_EQUALS(5, e.size())
    TEST_ASSERT_EQUALS(5, f.size())
}

3.2 assigns

void UnorderedMapSuite::assigns()
{
    std::unordered_map<std::string, std::string> a;
    std::unordered_map<std::string, std::string> b;
    std::unordered_map<std::string, std::string> c;
    std::unordered_map<std::string, std::string> d;
    a = { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    b = { { "mango", "yellow" }, { "strawberry", "red" } };
    c = merge_map(a, b);
    d = c;
    
    TEST_ASSERT_EQUALS(3, a.size())
    TEST_ASSERT_EQUALS(2, b.size())
    TEST_ASSERT_EQUALS(5, c.size())
    TEST_ASSERT_EQUALS(5, d.size())
}

3.3 iterators

void UnorderedMapSuite::iterators()
{
    std::unordered_map<std::string, std::string> names = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };

    int count = 0;
    for(auto it = names.begin(); it != names.end(); ++it)
    {
        std::cerr << it->first << ":" << it->second << " ";
        count++;
    }
    std::cerr << std::endl;
    TEST_ASSERT_EQUALS(3, count)
    count = 0;
    for(auto it = names.cbegin(); it != names.cend(); ++it)
    {
        std::cerr << it->first << ":" << it->second << " ";
        count++;
    }
    std::cerr <<std::endl;
    TEST_ASSERT_EQUALS(3, count)
}

说明:

  • 无序映射的迭代器是前向迭代器,只能从头到尾访问。所以没有rbegin/rend/crbegin/crend

3.4 capacity

void UnorderedMapSuite::capacity()
{
    std::unordered_map<std::string, std::string> a;
    std::unordered_map<std::string, std::string> b(
        { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } });

    TEST_ASSERT_EQUALS(true, a.empty())
    TEST_ASSERT_EQUALS(3, b.size())
    TEST_ASSERT_EQUALS(a.max_size(), b.max_size())
}

3.5 access

void UnorderedMapSuite::access()
{
    std::unordered_map<char, int> a;
    std::unordered_map<char, int> b = { { 'a', 10 }, { 'b', 20 }};
    bool hasExcpetion = false;
    a['a'] = 10;
    a['b'] = 20;

    b.at('a') = 20;
    b.at('b') = 30;

    try
    {
        b.at('c') = 40;
    }
    catch(...)
    {
        hasExcpetion = true;
    }
    TEST_ASSERT_EQUALS(10, a['a'])
    TEST_ASSERT_EQUALS(20, a['b'])
    TEST_ASSERT_EQUALS(20, b['a'])
    TEST_ASSERT_EQUALS(30, b['b'])
    TEST_ASSERT_EQUALS(true, hasExcpetion)
}

3.6 find/count/equal_range

void UnorderedMapSuite::find()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    auto it = fruits.find("apple");

    TEST_ASSERT_EQUALS("apple", it->first)
    it = fruits.find("God");
    TEST_ASSERT_EQUALS(true, it == fruits.end())
}

void UnorderedMapSuite::count()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };

    TEST_ASSERT_EQUALS(1, fruits.count("banana"))
    TEST_ASSERT_EQUALS(0, fruits.count("watermelon"))
}

void UnorderedMapSuite::equal_range()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    auto range = fruits.equal_range("pear");
    TEST_ASSERT_EQUALS("pear", range.first->first)
    TEST_ASSERT_EQUALS(true, range.first != range.second)
    range = fruits.equal_range("watermelon");
    TEST_ASSERT_EQUALS(true, range.first == fruits.end())
    TEST_ASSERT_EQUALS(true, range.first == range.second)
}

3.7 emplace

void UnorderedMapSuite::emplace()
{
    std::unordered_map<std::string, std::string> fruits;

    auto r = fruits.emplace("apple", "red");
    TEST_ASSERT_EQUALS("apple", r.first->first)
    TEST_ASSERT_EQUALS(true, r.second)
    TEST_ASSERT_EQUALS(1, fruits.size())

    r = fruits.emplace("pear", "yellow");
    TEST_ASSERT_EQUALS("pear", r.first->first)
    TEST_ASSERT_EQUALS(true, r.second)
    TEST_ASSERT_EQUALS(2, fruits.size())

    r = fruits.emplace("pear", "yellow");
    TEST_ASSERT_EQUALS("pear", r.first->first)
    TEST_ASSERT_EQUALS(false, r.second)
    TEST_ASSERT_EQUALS(2, fruits.size())
}
  • 返回值为std::pair对象,pair::first指向新加入元素或无序映射中已存在元素,pair::second为true说明插入成功,为false说明无序映射中已有该元素

3.8 emplace_hint

void UnorderedMapSuite::emplace_hint()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" } }; 

    auto it = fruits.emplace_hint(fruits.begin(), "pear", "yellow");
    TEST_ASSERT_EQUALS("pear", it->first)
    TEST_ASSERT_EQUALS(2, fruits.size())

    it = fruits.emplace_hint(fruits.begin(), "pear", "yellow");
    TEST_ASSERT_EQUALS("pear", it->first)
    TEST_ASSERT_EQUALS(2, fruits.size())
}

说明:

  • 返回值为iterator对象,指向新加入元素或无序映射中已存在元素。

3.9 insert

void UnorderedMapSuite::insert()
{
    std::unordered_map<std::string, std::string> a;
    std::unordered_map<std::string, std::string> b;
    std::unordered_map<std::string, std::string> c;
    auto r = a.insert({ "apple", "red" });
    TEST_ASSERT_EQUALS("apple", r.first->first)
    TEST_ASSERT_EQUALS(true, r.second)
    r = a.insert({ "apple", "red" });//no insert
    TEST_ASSERT_EQUALS("apple", r.first->first)
    TEST_ASSERT_EQUALS(false, r.second)

    auto it = a.insert(a.begin(), { "pear", "yellow" });
    TEST_ASSERT_EQUALS("pear", it->first)
    TEST_ASSERT_EQUALS(2, a.size())

    it = a.insert(a.begin(), { "pear", "yellow" });//no insert
    TEST_ASSERT_EQUALS("pear", it->first)
    TEST_ASSERT_EQUALS(2, a.size())

    std::string yellow = "yellow";
    a.insert({ yellow + " banana", "yellow"});
    b.insert(a.begin(), a.end());
    c.insert({{ "apple", "red" },  { "banana", "yellow" }, { "banana", "yellow" }});
    TEST_ASSERT_EQUALS(3, a.size())
    TEST_ASSERT_EQUALS(3, b.size())
    TEST_ASSERT_EQUALS(2, c.size())
}
  • 不带位置参数插入函数,返回std::pair对象,pair::first指新加入元素或无序映射中已存在元素,pair::second为true说明插入成功,为false说明无序映射中已有该元素
  • 带位置参数插入函数,返回iterator,指向新加入元素或无序映射中已存在元素。

3.10 erase

void UnorderedMapSuite::erase()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };

    TEST_ASSERT_EQUALS(3, fruits.size())

    fruits.erase(fruits.find("apple"));
    TEST_ASSERT_EQUALS(2, fruits.size())

    auto size = fruits.erase("banana");
    TEST_ASSERT_EQUALS(1, size)
    TEST_ASSERT_EQUALS(1, fruits.size())

    fruits.erase(fruits.begin(), fruits.end());
    TEST_ASSERT_EQUALS(0, fruits.size())
}

说明:

  • 按值删除返回删除元素个数
  • 按位置删除返回iterator指向被删除元素的下一位置

3.11 clear

void UnorderedMapSuite::clear()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };

    TEST_ASSERT_EQUALS(3, fruits.size())

    fruits.clear();
    TEST_ASSERT_EQUALS(0, fruits.size())
}

3.12 swap

void UnorderedMapSuite::swap()
{
    std::unordered_map<std::string, std::string> a;
    std::unordered_map<std::string, std::string> b = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };

    TEST_ASSERT_EQUALS(0, a.size())
    TEST_ASSERT_EQUALS(3, b.size())

    a.swap(b);
    TEST_ASSERT_EQUALS(3, a.size())
    TEST_ASSERT_EQUALS(0, b.size())
}

3.13 bucket_count

void UnorderedMapSuite::bucket_count()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    TEST_ASSERT_EQUALS(true, fruits.bucket_count() >= fruits.size())
}

说明:

  • 返回桶数量,桶数量大于等于元素个数

3.14 max_bucket_count

void UnorderedMapSuite::max_bucket_count()
{
    std::unordered_map<std::string, std::string> a;
    std::unordered_map<std::string, std::string> b = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    TEST_ASSERT_EQUALS(b.max_bucket_count(), a.max_bucket_count())
}

3.15 bucket_size

void UnorderedMapSuite::bucket_size()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    auto count = fruits.bucket_count();
    uint32_t size = 0;
    for(auto i = 0; i < count; i++)
        size += fruits.bucket_size(i);
    TEST_ASSERT_EQUALS(fruits.size(), size)
}

说明:

  • 返回指定桶中元素数量

3.16 bucket

void UnorderedMapSuite::bucket()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    auto count = fruits.bucket_count();
    for(auto fruit: fruits)
    {
        TEST_ASSERT_EQUALS(true, fruits.bucket(fruit.first) < count)
        std::cerr << fruit.first << " is in bucket #" << fruits.bucket(fruit.first) << std::endl;
    }
    std::cerr  << "name is in bucket #" << fruits.bucket("name") << std::endl;
}

说明:

  • 根据Key返回所在的桶索引(不管key有没有在集合中,都会返回桶索引)

3.17 load_factor

void UnorderedMapSuite::load_factor()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    float load_factor = static_cast<float>(fruits.size()) / static_cast<float>(fruits.bucket_count());
    TEST_ASSERT_EQUALS(load_factor, fruits.load_factor())
}

说明:

  • 返回负载率,等于元素个数/桶个数

3.18 max_load_factor

void UnorderedMapSuite::max_load_factor()
{
    std::unordered_map<std::string, std::string> fruits = 
    { { "apple", "red" }, { "pear", "yellow" }, { "banana", "yellow" }, { "banana", "yellow" } };
    float max_load_factor = fruits.max_load_factor();
    TEST_ASSERT_EQUALS(1.0, max_load_factor)
    uint32_t bucket_count = fruits.bucket_count();
    max_load_factor /= 2.0;
    fruits.max_load_factor(max_load_factor);
    TEST_ASSERT_EQUALS(max_load_factor, fruits.max_load_factor())
    TEST_ASSERT_EQUALS(true, fruits.bucket_count() > bucket_count)
}

说明:

  • 返回最大负载率,默认为1.0
  • 设置最大负载率,容器使用最大负载率值作为阈值,来增加bucket数量
  • 如果load_factor大于max_load_factor,容器将自动增加bucket数量

3.19 rehash

将容器中的桶数设置为n或更多。

void UnorderedMapSuite::rehash()
{
    std::unordered_map<std::string, std::string> a;

    a.rehash(12);
    TEST_ASSERT_EQUALS(true, a.bucket_count() > 12)

    a.insert({ "apple", "red" });
    a.insert({ "pear", "yellow" });
    a.insert({ "banana", "yellow" });
    a.insert({ "banana", "yellow" });

    uint32_t bucket_count = a.bucket_count();
    a.rehash(11);
    TEST_ASSERT_EQUALS(bucket_count, a.bucket_count())

    a.rehash(5);
    TEST_ASSERT_EQUALS(true, a.bucket_count() < bucket_count)
}

说明:

  • 如果n大于容器中当前存储桶的数量(bucket_count),则强制进行重新散列。新的存储桶计数可以等于或大于n。
  • 如果n低于容器中的当前桶数(bucket_count),则该函数可能对桶数没有影响,也可能不会强制重新散列。

3.20 reserve

将容器中的bucket数量(bucket_count)设置为最适合包含至少n个元素的数量。

void UnorderedMapSuite::reserve()
{
    std::unordered_map<std::string, std::string> a;

    a.reserve(5);
    TEST_ASSERT_EQUALS(true, a.bucket_count() > 5)

    a.insert({ "apple", "red" });
    a.insert({ "pear", "yellow" });
    a.insert({ "banana", "yellow" });
    a.insert({ "banana", "yellow" });
    TEST_ASSERT_EQUALS(true, a.bucket_count() > 4)
}

说明:

  • 如果n大于当前bucket_count乘以max_load_factor,则容器的bucket_count会增加,并强制重新散列。
  • 如果n低于该值,则该函数可能没有效果。

3.21 hash_function

void UnorderedMapSuite::hash_function()
{
    std::unordered_map<int, std::string> a;
    std::unordered_map<std::string, std::string> b;
    auto a_fun = a.hash_function();
    auto b_fun = b.hash_function();

    TEST_ASSERT_EQUALS(100, a_fun(100))
    TEST_ASSERT_EQUALS(200, a_fun(200))
    std::cerr << "Rose: " <<  b_fun("Rose") << std::endl;
    std::cerr << "James: " <<  b_fun("James") << std::endl;
}

3.22 key_eq

void UnorderedMapSuite::key_eq()
{
    std::unordered_map<int, std::string> a;
    std::unordered_map<std::string, std::string> b;
    auto a_fun = a.key_eq();
    auto b_fun = b.key_eq();

    TEST_ASSERT_EQUALS(true, a_fun(10, 10))
    TEST_ASSERT_EQUALS(false, a_fun(10, 20))

    TEST_ASSERT_EQUALS(true, b_fun("this", "this"))
    TEST_ASSERT_EQUALS(false, b_fun("this", "that"))
}

3.23 get_allocator

void UnorderedMapSuite::get_allocator()
{
    std::unordered_map<int, std::string> a;
    auto allocator = a.get_allocator();
    std::pair<const int, std::string> *p = allocator.allocate(5);

    TEST_ASSERT_EQUALS(true, p != nullptr)
    allocator.deallocate(p, 5);

    try
    {
        p = allocator.allocate(allocator.max_size() + 1);
    }
    catch(...)
    {
        p = nullptr;
    }
    TEST_ASSERT_EQUALS(true, p == nullptr)
}
  • 13
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flysnow010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值