SGISTL源码阅读二十四 hash_set 关联式容器
前言
前面我们已经分析了set
和hashtable
,set
的底层容器是红黑树,而接下来我们要分析的hash_set
,是底层为hashtable
的集合。
深入源码
hash_set
的定义部分
#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class Value, class HashFcn = hash<Value>,
class EqualKey = equal_to<Value>,
class Alloc = alloc>
#else
template <class Value, class HashFcn, class EqualKey, class Alloc = alloc>
#endif
class hash_set
{
private:
//定义别名
typedef hashtable<Value, Value, HashFcn, identity<Value>,
EqualKey, Alloc> ht;
//定义一个hashtable类型的成员变量rep
ht rep;
public:
//声明相应型别
//hashtable有一些无法处理的型别
//除非用户为那些型别撰写 hash function
typedef typename ht::key_type key_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::const_pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::const_reference reference;
typedef typename ht::const_reference const_reference;
//直接使用了底层hashtable的迭代器
typedef typename ht::const_iterator iterator;
typedef typename ht::const_iterator const_iterator;
hash_set
的构造函数
它的构造函数版本非常地多,但是实现却非常简单,直接简单地调用了底层hashtable
提供的方法
public:
//默认构造,默认大小为100个buckets(hashtable内部会将100转换为一个合适的质数)
hash_set() : rep(100, hasher(), key_equal()) {}
//指定大小创建,防止隐式转换
explicit hash_set(size_type n) : rep(n, hasher(), key_equal()) {}
//指定大小n以及哈希函数,其余取默认值
hash_set(size_type n, const hasher& hf) : rep(n, hf, key_equal()) {}
//指定大小n及哈希函数和比较key的大小的函数
hash_set(size_type n, const hasher& hf, const key_equal& eql)
: rep(n, hf, eql) {}
#ifdef __STL_MEMBER_TEMPLATES
//我们需要注意到下面所有的插入都是调用的insert_unique
//保证了set中元素的唯一性
template <class InputIterator>
hash_set(InputIterator f, InputIterator l)
: rep(100, hasher(), key_equal()) { rep.insert_unique(f, l); }
template <class InputIterator>
hash_set(InputIterator f, InputIterator l, size_type n)
: rep(n, hasher(), key_equal()) { rep.insert_unique(f, l); }
template <class InputIterator>
hash_set(InputIterator f, InputIterator l, size_type n,
const hasher& hf)
: rep(n, hf, key_equal()) { rep.insert_unique(f, l); }
template <class InputIterator>
hash_set(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const key_equal& eql)
: rep(n, hf, eql) { rep.insert_unique(f, l); }
#else
//以下为const版本
hash_set(const value_type* f, const value_type* l)
: rep(100, hasher(), key_equal()) { rep.insert_unique(f, l); }
hash_set(const value_type* f, const value_type* l, size_type n)
: rep(n, hasher(), key_equal()) { rep.insert_unique(f, l); }
hash_set(const value_type* f, const value_type* l, size_type n,
const hasher& hf)
: rep(n, hf, key_equal()) { rep.insert_unique(f, l); }
hash_set(const value_type* f, const value_type* l, size_type n,
const hasher& hf, const key_equal& eql)
: rep(n, hf, eql) { rep.insert_unique(f, l); }
hash_set(const_iterator f, const_iterator l)
: rep(100, hasher(), key_equal()) { rep.insert_unique(f, l); }
hash_set(const_iterator f, const_iterator l, size_type n)
: rep(n, hasher(), key_equal()) { rep.insert_unique(f, l); }
hash_set(const_iterator f, const_iterator l, size_type n,
const hasher& hf)
: rep(n, hf, key_equal()) { rep.insert_unique(f, l); }
hash_set(const_iterator f, const_iterator l, size_type n,
const hasher& hf, const key_equal& eql)
: rep(n, hf, eql) { rep.insert_unique(f, l); }
#endif /*__STL_MEMBER_TEMPLATES */
hashset
的相关操作
这些操作也是一样,直接调用了底层hashtable
所实现的方法。
基本操作
public:
//返回大小
size_type size() const { return rep.size(); }
size_type max_size() const { return rep.max_size(); }
//判断是否为空
bool empty() const { return rep.empty(); }
//交换两个hashset
void swap(hash_set& hs) { rep.swap(hs.rep); }
friend bool operator== __STL_NULL_TMPL_ARGS (const hash_set&,
const hash_set&);
//返回头部的迭代器
iterator begin() const { return rep.begin(); }
//返回尾部的迭代器
iterator end() const { return rep.end(); }
//...
//查找值为key的节点,返回一个迭代器
iterator find(const key_type& key) const { return rep.find(key); }
//计算值为key的节点个数
size_type count(const key_type& key) const { return rep.count(key); }
pair<iterator, iterator> equal_range(const key_type& key) const
{ return rep.equal_range(key); }
//...
public:
//重建(如果hint小于当前hashset大小,则不会重建)
void resize(size_type hint) { rep.resize(hint); }
//返回桶的数量
size_type bucket_count() const { return rep.bucket_count(); }
size_type max_bucket_count() const { return rep.max_bucket_count(); }
//返回节点数
size_type elems_in_bucket(size_type n) const
{ return rep.elems_in_bucket(n); }
插入操作
public:
pair<iterator, bool> insert(const value_type& obj)
{
//确保set中元素的唯一性,调用insert_unique()
pair<typename ht::iterator, bool> p = rep.insert_unique(obj);
//pair的第一个值为迭代器,如果插入失败,则返回指向hashset中值为obj的迭代器,插入成功则指向插入成功的位置
//第二个值为布尔类型,插入成功则返回true,插入失败则返回false
return pair<iterator, bool>(p.first, p.second);
}
#ifdef __STL_MEMBER_TEMPLATES
//迭代器指定范围插入
template <class InputIterator>
void insert(InputIterator f, InputIterator l) { rep.insert_unique(f,l); }
#else
void insert(const value_type* f, const value_type* l) {
rep.insert_unique(f,l);
}
//const版本
void insert(const_iterator f, const_iterator l) {rep.insert_unique(f, l); }
#endif /*__STL_MEMBER_TEMPLATES */
//noresize版本
pair<iterator, bool> insert_noresize(const value_type& obj)
{
pair<typename ht::iterator, bool> p = rep.insert_unique_noresize(obj);
return pair<iterator, bool>(p.first, p.second);
}
删除操作
//删除值为key的节点,并返回删除了的个数
size_type erase(const key_type& key) {return rep.erase(key); }
//删除指定位置的节点
void erase(iterator it) { rep.erase(it); }
//删除指定范围的节点
void erase(iterator f, iterator l) { rep.erase(f, l); }
//将hashset清空
void clear() { rep.clear(); }
操作符重载
//重载==
//直接调用hashtable所实现的
template <class Value, class HashFcn, class EqualKey, class Alloc>
inline bool operator==(const hash_set<Value, HashFcn, EqualKey, Alloc>& hs1,
const hash_set<Value, HashFcn, EqualKey, Alloc>& hs2)
{
return hs1.rep == hs2.rep;
}
关于hash_multiset
hash_multiset
和 hash_set
的区别等同于multiset
和 set
的区别。
也是由于调用inset
的函数不同而造成的。
深入源码
下面只贴出了部分代码
//调用了rep.insert_equal
#ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
hash_multiset(InputIterator f, InputIterator l)
: rep(100, hasher(), key_equal()) { rep.insert_equal(f, l); }
template <class InputIterator>
hash_multiset(InputIterator f, InputIterator l, size_type n)
: rep(n, hasher(), key_equal()) { rep.insert_equal(f, l); }
template <class InputIterator>
hash_multiset(InputIterator f, InputIterator l, size_type n,
const hasher& hf)
: rep(n, hf, key_equal()) { rep.insert_equal(f, l); }
template <class InputIterator>
hash_multiset(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const key_equal& eql)
: rep(n, hf, eql) { rep.insert_equal(f, l); }
#else
//...
public:
iterator insert(const value_type& obj) { return rep.insert_equal(obj); }
#ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
void insert(InputIterator f, InputIterator l) { rep.insert_equal(f,l); }
#else
void insert(const value_type* f, const value_type* l) {
rep.insert_equal(f,l);
}
void insert(const_iterator f, const_iterator l) { rep.insert_equal(f, l); }
总结
本次我们分析了hash_set
以及hash_multiset
,可以看到它们底层是由hashtable
实现的,他们大量地调用了hashtable
所实现的方法。