【C++】关联容器(四):无序容器

11.4 无序容器

C++ 11 标准定义了 4 个无序关联容器。这些容器不使用比较运算符来组织元素,而是使用一个哈希函数和关键字类型的 == 运算符。在关键字类型的元素没有明显的序关系的情况下,无序容器是非常有用的。在某些应用中,维护元素的序代价非常高,此时无序容器也非常好用。

使用无序容器

除了使用哈希函数来对元素进行组织以外,无序容器提供了与有序容器相同的操作。这意味着曾用于 map 和 set 的操作也可以用于 unordered_map 和 unordered_set。类似地,无序容器也有自己的允许重复关键字的版本。

管理桶

无序容器在存储上被组织为一组桶,每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。为了访问一个元素,容器首先计算元素的哈希值,它指出应该搜索哪个桶。容器将一个具有特定哈希值的所有元素保存到相同的桶中。如果容器允许重复关键字,所有具有相同关键字的元素也都会在同一个桶中。因此,无需容器的性能依赖于哈希函数的质量和桶的数量和大小。

对于相同的参数,哈希函数必须总是产生相同的结果。

无需函数提供了一组管理桶的函数。这些函数允许我们查询容器的状态并在必要时强制容器进行重组:
【C++】关联容器(四):无序容器_第1张图片

无序容器对关键字类型的要求

默认情况下,无序容器使用关键字类型的 == 运算符来比较元素,它们还是用一个 hash 类型的对象来生成每个元素的哈希值。

标准库已经为内置类型(包括指针)提供了 hash 模板。同时,还为一些标准库类型,包括 string 以及智能指针定义了 hash。因此我们可以直接定义关键字是内置类型(包括指针类型)、string 以及智能指针类型的无序容器。

但是我们不能直接定义关键字类型为自定义类类型的无序容器(因为自定义类类型没有 hash 的定义)。与容器不同,不能直接使用哈希模板,而必须提供我们自己的 hash 模板版本。

此处不使用默认的 hash,而是使用另一种方法,类似于为有序容器重载关键字类型的默认比较操作。为了能够将之前文章中所提到的 Sales_data 作为定义无需容器的关键字,我们需要提供函数来替代 == 运算符和哈希值计算函数。我们从定义这些重载函数开始:

size_t hasher(const Sales_data &sd) {
	return hash<string>() (sd.isbn());
}	// 提供 hash 操作
bool eqOp(const Sales_data &lhs, const Sales_data &rhs) {
	return lhs.isbn() == rhs.isbn();
}	// 提供相等的比较

我们的 hasher 函数使用一个标准库 hash 类型对象来计算 ISBN 成员的哈希值,该 hash 类型建立在 string 类型值上。类似的,eqOp 函数通过比较 ISBN 号来比较两个 Sales_data。

使用上述函数来定义 unordered_multiset:

using SD_multiset = unordered_multiset<Sales_data, decltype(hasher)*, decltype(eqOp)*>;
SD_multiset bookstore(42, hasher, eqOp);

如果我们的类定义了 == 运算符,则可以只重载哈希函数:

// 使用 FOoHash 生成哈希函数, Foo 必须有 == 运算符
unordered_set<Foo, decltype(FooHash)*> fooSet(10, FooHash);

(综上所述,想要为自定义类类型建立无序关联容器并将它作为关键字,那么它必须有 == 的定义(可以是一个函数),以及哈希函数的定义)

你可能感兴趣的:(cpp,c++)