C++容器之无序集(std::unordered_set)

1 概述

  无序集是不按特定顺序存储唯一元素的容器,允许根据单个元素的值快速检索这些元素。
  在一个无序集合中,元素的值同时也是唯一标识它的键。键是不可变的,因此,不能在容器中修改无序集中的元素,但它们可以插入和移除。
  在内部,无序集中的元素不按任何特定顺序排序,而是根据其哈希值组织到桶中,以允许直接根据其值快速访问各个元素(平均具有恒定的平均时间复杂度)。
  无序集合容器比集合容器更快地通过关键字访问单个元素,尽管它们通常在通过元素子集进行范围迭代时效率较低。
  容器中的迭代器至少是前向迭代器。

容器特性:

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

2 使用实例

void UnorderedSetSuite::rehash()
{
    std::unordered_set<std::string> a;

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

    a.insert("James");
    a.insert("Tom");
    a.insert("Jim");
    a.insert("Rose");
    a.insert("Geore");

    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_set(T const & a, T const& b)
{
    T t(a);
    t.insert(b.begin(), b.end());
    return t;
}

void UnorderedSetSuite::construct()
{
    std::unordered_set<int> a;
    std::unordered_set<int> b({ 1, 2, 3, 3 });
    std::unordered_set<int> c({ 4, 5, 6, 6 });
    std::unordered_set<int> d(b);
    std::unordered_set<int> e(merge_set(b, c));
    std::unordered_set<int> f(e.begin(), e.end());

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

3.2 assigns

void UnorderedSetSuite::assigns()
{
    std::unordered_set<int> a;
    std::unordered_set<int> b;
    std::unordered_set<int> c;
    std::unordered_set<int> d;
    a = { 1, 2, 3, 3 };
    b = { 4, 5, 6, 6 };
    c = merge_set(a, b);
    d = c;
    
    TEST_ASSERT_EQUALS(3, a.size())
    TEST_ASSERT_EQUALS(3, b.size())
    TEST_ASSERT_EQUALS(6, c.size())
    TEST_ASSERT_EQUALS(6, d.size())
}

3.3 iterators

void UnorderedSetSuite::iterators()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };

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

说明:

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

3.4 capacity

void UnorderedSetSuite::capacity()
{
    std::unordered_set<int> a;
    std::unordered_set<int> b({ 1, 2, 3, 3 });

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

3.5 find

void UnorderedSetSuite::find()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };
    auto it = names.find("Geore");

    TEST_ASSERT_EQUALS("Geore", *it)
    it = names.find("God");
    TEST_ASSERT_EQUALS(true, it == names.end())
}

3.6 count

void UnorderedSetSuite::count()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };

    TEST_ASSERT_EQUALS(1, names.count("Geore"))
    TEST_ASSERT_EQUALS(0, names.count("God"))
}

3.7 equal_range

void UnorderedSetSuite::equal_range()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" }; 
    auto range = names.equal_range("Geore");
    TEST_ASSERT_EQUALS("Geore", *range.first)
    TEST_ASSERT_EQUALS(true, range.first != range.second)
    range = names.equal_range("Ji");
    TEST_ASSERT_EQUALS(true, range.first == names.end())
    TEST_ASSERT_EQUALS(true, range.first == range.second)
}

3.8 emplace

void UnorderedSetSuite::emplace()
{
    std::unordered_set<std::string> names;

    auto r = names.emplace("James");
    TEST_ASSERT_EQUALS("James", *r.first)
    TEST_ASSERT_EQUALS(true, r.second)
    TEST_ASSERT_EQUALS(1, names.size())

    r = names.emplace("Geore");
    TEST_ASSERT_EQUALS("Geore", *r.first)
    TEST_ASSERT_EQUALS(true, r.second)
    TEST_ASSERT_EQUALS(2, names.size())

    r = names.emplace("Geore");
    TEST_ASSERT_EQUALS("Geore", *r.first)
    TEST_ASSERT_EQUALS(false, r.second)
    TEST_ASSERT_EQUALS(2, names.size())
}

说明:

  • 返回值为std::pair对象,pair::first指向新加入元素或集合中已存在元素,pair::second为true说明插入成功,为false说明集合中已有该元素

3.9 emplace_hint

void UnorderedSetSuite::emplace_hint()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom" }; 

    auto it = names.emplace_hint(names.begin(), "Geore");
    TEST_ASSERT_EQUALS("Geore", *it)
    TEST_ASSERT_EQUALS(3, names.size())

    it = names.emplace_hint(names.begin(), "Geore");
    TEST_ASSERT_EQUALS("Geore", *it)
    TEST_ASSERT_EQUALS(3, names.size())
}

说明:

  • 返回值为iterator对象,指向新加入元素或集合中已存在元素。

3.10 insert

void UnorderedSetSuite::insert()
{
    std::unordered_set<std::string> a;
    std::unordered_set<std::string> b;
    std::unordered_set<std::string> c;
    std::string str = "Tom";
    auto r = a.insert(str);
    TEST_ASSERT_EQUALS("Tom", *r.first)
    TEST_ASSERT_EQUALS(true, r.second)
    r = a.insert(str);//no insert
    TEST_ASSERT_EQUALS("Tom", *r.first)
    TEST_ASSERT_EQUALS(false, r.second)

    auto it = a.insert(a.begin(), "Geore");
    TEST_ASSERT_EQUALS("Geore", *it)
    TEST_ASSERT_EQUALS(2, a.size())

    it = a.insert(a.begin(), "Geore");//no insert
    TEST_ASSERT_EQUALS("Geore", *it)
    TEST_ASSERT_EQUALS(2, a.size())


    a.insert(str + " Cruise");
    b.insert(a.begin(), a.end());
    c.insert({"Tom", "James", "James"});
    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.11 erase

void UnorderedSetSuite::erase()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };

    TEST_ASSERT_EQUALS(5, names.size())

    names.erase(names.find("James"));//"Tom", "Jim", "Rose", "Geore"
    TEST_ASSERT_EQUALS(4, names.size())

    auto size = names.erase("Geore");
    TEST_ASSERT_EQUALS(1, size)
    TEST_ASSERT_EQUALS(3, names.size())

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

说明:

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

3.12 clear

void UnorderedSetSuite::clear()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };

    TEST_ASSERT_EQUALS(5, names.size())

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

3.13 swap

void UnorderedSetSuite::swap()
{
    std::unordered_set<int> a;
    std::unordered_set<int> b({ 1, 2, 3, 3 });

    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.14 bucket_count

void UnorderedSetSuite::bucket_count()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };
    TEST_ASSERT_EQUALS(true, names.bucket_count() >= names.size())
}

说明:

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

3.15 max_bucket_count

void UnorderedSetSuite::max_bucket_count()
{
    std::unordered_set<int> a;
    std::unordered_set<int> b({ 1, 2, 3, 3 });
    TEST_ASSERT_EQUALS(b.max_bucket_count(), a.max_bucket_count())
}

3.16 bucket_size

void UnorderedSetSuite::bucket_size()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };
    auto count = names.bucket_count();
    uint32_t size = 0;
    for(auto i = 0; i < count; i++)
        size += names.bucket_size(i);
    TEST_ASSERT_EQUALS(names.size(), size)
}

说明:

  • 返回指定桶中元素数量

3.17 bucket

void UnorderedSetSuite::bucket()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };
    auto count = names.bucket_count();
    for(auto name: names)
    {
        TEST_ASSERT_EQUALS(true, names.bucket(name) < count)
        std::cerr << name << " is in bucket #" << names.bucket(name) << std::endl;
    }
    std::cerr  << "name is in bucket #" << names.bucket("name") << std::endl;
}

说明:

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

3.18 load_factor

void UnorderedSetSuite::load_factor()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };
    float load_factor = static_cast<float>(names.size()) / static_cast<float>(names.bucket_count());
    TEST_ASSERT_EQUALS(load_factor, names.load_factor())
}

说明:

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

3.19 max_load_factor

void UnorderedSetSuite::max_load_factor()
{
    std::unordered_set<std::string> names = 
    { "James", "Tom", "Jim", "Rose", "Geore",  "Geore" };
    float max_load_factor = names.max_load_factor();
    TEST_ASSERT_EQUALS(1.0, max_load_factor)
    uint32_t bucket_count = names.bucket_count();
    max_load_factor /= 2.0;
    names.max_load_factor(max_load_factor);
    TEST_ASSERT_EQUALS(max_load_factor, names.max_load_factor())
    TEST_ASSERT_EQUALS(true, names.bucket_count() > bucket_count)
}

说明:

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

3.20 rehash

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

void UnorderedSetSuite::rehash()
{
    std::unordered_set<std::string> a;

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

    a.insert("James");
    a.insert("Tom");
    a.insert("Jim");
    a.insert("Rose");
    a.insert("Geore");

    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.21 reserve

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

void UnorderedSetSuite::reserve()
{
    std::unordered_set<std::string> a;

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

    a.insert("James");
    a.insert("Tom");
    a.insert("Jim");
    a.insert("Rose");
    a.insert("Geore");
    TEST_ASSERT_EQUALS(true, a.bucket_count() > 5)
}

说明:

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

3.22 hash_function

void UnorderedSetSuite::hash_function()
{
    std::unordered_set<int> a;
    std::unordered_set<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.23 key_eq

void UnorderedSetSuite::key_eq()
{
    std::unordered_set<int> a;
    std::unordered_set<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.24 get_allocator

void UnorderedSetSuite::get_allocator()
{
    std::unordered_set<int> a;
    auto allocator = a.get_allocator();
    int *p = allocator.allocate(5);

    TEST_ASSERT_EQUALS(true, p != nullptr)
    for(int i = 1; i <= 5; i++)
       p[i] = i * 10;
    
    allocator.deallocate(p, 5);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

flysnow010

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

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

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

打赏作者

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

抵扣说明:

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

余额充值