目录
- 1 概述
- 2 使用实例
- 3 接口使用
- 3.1 construct
- 3.2 assigns
- 3.3 iterators
- 3.4 capacity
- 3.5 find
- 3.6 count
- 3.7 equal_range
- 3.8 emplace
- 3.9 emplace_hint
- 3.10 insert
- 3.11 erase
- 3.12 clear
- 3.13 swap
- 3.14 bucket_count
- 3.15 max_bucket_count
- 3.16 bucket_size
- 3.17 bucket
- 3.18 load_factor
- 3.19 max_load_factor
- 3.20 rehash
- 3.21 reserve
- 3.22 hash_function
- 3.23 key_eq
- 3.24 get_allocator
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)
}