目录
- 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 概述
无序多集是不按特定顺序存储元素的容器,允许根据单个元素的值快速检索这些元素,与无序集容器非常相似,但允许不同的元素具有相同的值。
在一个无序的多重集合中,一个元素的值同时也是用来识别它的键。键是不可变的,因此,无序的多重集中的元素不能在容器中修改一次,它们可以插入和删除。
在内部,undered_multiset中的元素没有任何特定的排序,而是根据它们的哈希值组织到桶中,以允许直接根据它们的值快速访问各个元素(平均具有恒定的平均时间复杂度)。
具有等效值的元素被分组在同一个bucket中,并且迭代器(请参阅equal_range)可以遍历所有元素。
容器中的迭代器至少是前向迭代器。
容器特性:
- 关联性 关联容器中的元素由它们的键(Key)引用,而不是由它们在容器中的绝对位置引用。
- 无序性 无序容器使用哈希表来组织其元素,哈希表允许通过其键快速访问元素。
- 集合 元素的值也是用来识别它的键(Key)。
- 多值性 容器中的多个元素可以具有等效的键
- 分配器感知 容器使用分配器对象来动态处理其存储需求。
- 其类图如下:
2 使用实例
void UnorderedMultiSet::rehash()
{
std::unordered_multiset<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");
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_multiset(T const & a, T const& b)
{
T t(a);
t.insert(b.begin(), b.end());
return t;
}
void UnorderedMultiSet::construct()
{
std::unordered_multiset<int> a;
std::unordered_multiset<int> b({ 1, 2, 3, 3 });
std::unordered_multiset<int> c({ 4, 5, 6, 6 });
std::unordered_multiset<int> d(b);
std::unordered_multiset<int> e(merge_multiset(b, c));
std::unordered_multiset<int> f(e.begin(), e.end());
TEST_ASSERT_EQUALS(true, a.empty())
TEST_ASSERT_EQUALS(4, b.size())
TEST_ASSERT_EQUALS(4, c.size())
TEST_ASSERT_EQUALS(4, d.size())
TEST_ASSERT_EQUALS(8, e.size())
TEST_ASSERT_EQUALS(8, f.size())
}
3.2 assigns
void UnorderedMultiSet::assigns()
{
std::unordered_multiset<int> a;
std::unordered_multiset<int> b;
std::unordered_multiset<int> c;
std::unordered_multiset<int> d;
a = { 1, 2, 3, 3 };
b = { 4, 5, 6, 6 };
c = merge_multiset(a, b);
d = c;
TEST_ASSERT_EQUALS(4, a.size())
TEST_ASSERT_EQUALS(4, b.size())
TEST_ASSERT_EQUALS(8, c.size())
TEST_ASSERT_EQUALS(8, d.size())
}
3.3 iterators
void UnorderedMultiSet::iterators()
{
std::unordered_multiset<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(6, count)
count = 0;
for(auto it = names.cbegin(); it != names.cend(); ++it)
{
std::cerr << *it << " ";
count++;
}
std::cerr <<std::endl;
TEST_ASSERT_EQUALS(6, count)
}
说明:
- 无序集的迭代器是前向迭代器,只能从头到尾访问。所以没有rbegin/rend/crbegin/crend
3.4 capacity
void UnorderedMultiSet::capacity()
{
std::unordered_multiset<int> a;
std::unordered_multiset<int> b({ 1, 2, 3, 3 });
TEST_ASSERT_EQUALS(true, a.empty())
TEST_ASSERT_EQUALS(4, b.size())
TEST_ASSERT_EQUALS(a.max_size(), b.max_size())
}
3.5 find
void UnorderedMultiSet::find()
{
std::unordered_multiset<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 UnorderedMultiSet::count()
{
std::unordered_multiset<std::string> names =
{ "James", "Tom", "Jim", "Rose", "Geore", "Geore" };
TEST_ASSERT_EQUALS(2, names.count("Geore"))
TEST_ASSERT_EQUALS(0, names.count("God"))
}
3.7 equal_range
void UnorderedMultiSet::equal_range()
{
std::unordered_multiset<std::string> names =
{ "James", "Tom", "Jim", "Rose", "Geore", "Geore", "Geore" };
auto range = names.equal_range("Geore");
int count = 0;
for(auto it = range.first; it != range.second; ++it)
{
TEST_ASSERT_EQUALS("Geore", *it)
count++;
}
TEST_ASSERT_EQUALS(3, count)
}
3.8 emplace
void UnorderedMultiSet::emplace()
{
std::unordered_multiset<std::string> names;
auto it = names.emplace("James");
TEST_ASSERT_EQUALS("James", *it)
TEST_ASSERT_EQUALS(1, names.size())
it = names.emplace("Geore");
TEST_ASSERT_EQUALS("Geore", *it)
TEST_ASSERT_EQUALS(2, names.size())
it = names.emplace("Geore");
TEST_ASSERT_EQUALS("Geore", *it)
TEST_ASSERT_EQUALS(3, names.size())
}
说明:
- 返回值为iterator对象,指向新加入元素或集合中已存在元素。
3.9 emplace_hint
void UnorderedMultiSet::emplace_hint()
{
std::unordered_multiset<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(4, names.size())
}
说明:
- 返回值为iterator对象,指向新加入元素或集合中已存在元素。
3.10 insert
void UnorderedMultiSet::insert()
{
std::unordered_multiset<std::string> a;
std::unordered_multiset<std::string> b;
std::unordered_multiset<std::string> c;
std::string str = "Tom";
auto it = a.insert(str);
TEST_ASSERT_EQUALS("Tom", *it)
it = a.insert(str);
TEST_ASSERT_EQUALS("Tom", *it)
TEST_ASSERT_EQUALS(2, a.size())
it = a.insert(a.begin(), "Geore");
TEST_ASSERT_EQUALS("Geore", *it)
TEST_ASSERT_EQUALS(3, a.size())
it = a.insert(a.begin(), "Geore");
TEST_ASSERT_EQUALS("Geore", *it)
TEST_ASSERT_EQUALS(4, a.size())
a.insert(str + " Cruise");
b.insert(a.begin(), a.end());
c.insert({"Tom", "James", "James"});
TEST_ASSERT_EQUALS(5, a.size())
TEST_ASSERT_EQUALS(5, b.size())
TEST_ASSERT_EQUALS(3, c.size())
}
说明:
- 返回iterator,指向新加入元素或集合中已存在元素。
3.11 erase
void UnorderedMultiSet::erase()
{
std::unordered_multiset<std::string> names =
{ "James", "Tom", "Jim", "Rose", "Geore", "Geore" };
TEST_ASSERT_EQUALS(6, names.size())
names.erase(names.find("James"));//"Tom", "Jim", "Rose", "Geore", "Geore"
TEST_ASSERT_EQUALS(5, names.size())
auto size = names.erase("Geore");
TEST_ASSERT_EQUALS(2, size)
TEST_ASSERT_EQUALS(3, names.size())
names.erase(names.begin(), names.end());
TEST_ASSERT_EQUALS(0, names.size())
}
说明:
- 按值删除返回删除元素个数
- 按位置删除返回iterator指向被删除元素的下一位置
3.12 clear
void UnorderedMultiSet::clear()
{
std::unordered_multiset<std::string> names =
{ "James", "Tom", "Jim", "Rose", "Geore", "Geore" };
TEST_ASSERT_EQUALS(6, names.size())
names.clear();
TEST_ASSERT_EQUALS(0, names.size())
}
3.13 swap
void UnorderedMultiSet::swap()
{
std::unordered_multiset<int> a;
std::unordered_multiset<int> b({ 1, 2, 3, 3 });
TEST_ASSERT_EQUALS(0, a.size())
TEST_ASSERT_EQUALS(4, b.size())
a.swap(b);
TEST_ASSERT_EQUALS(4, a.size())
TEST_ASSERT_EQUALS(0, b.size())
}
3.14 bucket_count
void UnorderedMultiSet::bucket_count()
{
std::unordered_multiset<std::string> names =
{ "James", "Tom", "Jim", "Rose", "Geore", "Geore" };
TEST_ASSERT_EQUALS(true, names.bucket_count() >= names.size())
}
说明:
- 返回桶数量,桶数量大于等于元素个数
3.15 max_bucket_count
void UnorderedMultiSet::max_bucket_count()
{
std::unordered_multiset<int> a;
std::unordered_multiset<int> b({ 1, 2, 3, 3 });
TEST_ASSERT_EQUALS(b.max_bucket_count(), a.max_bucket_count())
}
3.16 bucket_size
void UnorderedMultiSet::bucket_size()
{
std::unordered_multiset<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 UnorderedMultiSet::bucket()
{
std::unordered_multiset<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 UnorderedMultiSet::load_factor()
{
std::unordered_multiset<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 UnorderedMultiSet::max_load_factor()
{
std::unordered_multiset<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 UnorderedMultiSet::rehash()
{
std::unordered_multiset<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");
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 UnorderedMultiSet::reserve()
{
std::unordered_multiset<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 UnorderedMultiSet::hash_function()
{
std::unordered_multiset<int> a;
std::unordered_multiset<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 UnorderedMultiSet::key_eq()
{
std::unordered_multiset<int> a;
std::unordered_multiset<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 UnorderedMultiSet::get_allocator()
{
std::unordered_multiset<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)
}