无序容器

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

虽然理论上哈希技术能获得更好的平均性能,但在实际中想要达到很好的效果还需要进行一些性能测试和调优工作。因此,使用无序容器通常更为简单。

 

使用无序容器

除了哈希管理操作之外,无序容器还提供了与有序容器相同的操作(find、insert等)。这意味着我们曾用于map和set的操作也能用于unordered_map和unordered_set。类似的,无序容器也有允许重复关键字的版本。

 

管理桶

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

对于相同的参数,哈希函数必须总是产生相同的结果。理想情况下,哈希函数还能将每个特定的值映射到唯一的桶。但是,将不同关键字的元素映射到相同的桶也是允许的。当一个桶保存多个元素时,需要顺序搜索这些元素来查找我们想要的那个。计算一个元素的哈希值和在桶中搜索通常都是很快的操作。但是,如果一个桶中保存了很多元素,那么查找一个特定元素就需要大量比较操作。

无序容器提供了一组管理桶的函数。如表所示。这些成员函数允许我们查询容器的状态以及在必要时强制容器进行重组。

无序容器管理操作

桶接口

c.bucket_count()        正在使用的桶的数目

c.max_bucket_count()       容器能容纳的最多的桶的数量

c.bucket_size(n)          第n个桶中有多少元素

c.bucket(k)            关键字为k的元素在哪个桶中

桶迭代

local_iterator          可以用来访问桶中元素的迭代器类型

const_local_iterator       桶迭代器的const版本

c.begin(n),c.end(n)         桶n的首元素迭代器和尾后迭代器

c.cbegin(n),c.cend(n)           返回const_local_iterator

哈希策略

c.load_factor()         每个桶的平均元素数量,返回float值

c.max_load_factor()       c试图维护的平均桶大小,返回float值,c会在需要时添加新的桶,以使得load_factor<=max_load_factor

c.rehash(n)          重组存储,使得bucket_count>=n且bucket_count>size/max_load_factor

c.reserve(n)          重组存储,使得c可以保存n个元素且不必rehash

 

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

默认情况下,无序容器使用关键字类型的==运算符来比较元素,它们还使用一个hash<key_type>类型的对象来生成每个元素的哈希值。标准库为内置类型(包括指针)提供了hash模板。还为一些标准库类型,包括string和我们将要介绍的智能指针类型定义了hash。因此,我们可以直接定义关键字是内置类型(包括指针类型)、string还是智能指针的无序容器。

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

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

size_t hasher(const Sales_data &sd)

{

  return hash<string>()(sd.isbn());

}

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,decltypr(hasher)*,decltype(eqOp)*>;

//参数是桶大小、哈希函数指针和相等型判断运算符指针

SD_multiset bookstore(42,hasher,eqOp);

为了简化bookstore的定义,首先为unordered_multiset定义了一个类型别名,此集合的哈希和相等性判断操作与hasher和eqOp函数有着相同的类型。通过使用这种类型,在定义bookstore时可以将我们希望它使用的函数的指针传递给它。

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

//使用FooHash生成哈希值;Foo必须有==运算符

unordered_set<Foo,decltype(FooHash)*> fooset(10,FooHash);

 

posted @ 2014-08-20 09:08  Jessica程序猿  阅读(1418)  评论(0编辑  收藏  举报