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