STL源代码剖析 容器 stl_hashtable.h
本文为senlie原创。转载请保留此地址:http://blog.csdn.net/zhengsenlie
hashtable
---------------------------------------------------------------------------
二叉搜索树具有对数平均时间的表现,它建立在输入数据有足够的随机性的如果
hashtable 有常数平均时间的表现,基于统计,不需依赖输入元素的随机性
hashtalbe 的简单实现:
所有元素都 16-bits 不带正负号的整数,范围 0~65535,配置一个 array。索引號码为 0~65535,初值所有为 0 。
每个元素值表示索引號相应的元素值出现的次数。
每插入元素 i ,运行 array[i]++; 每删除元素 i ,运行 array[i]--;
搜索元素 i ,仅仅要检查 array[i] 是否为 0
排序时,仅仅要遍历 array 。输入 array[i] 个 i
问题:
1.假设元素是字符串(或其他)而非整数。将无法被拿来作为 array 的索引
2.假设元素是 32-bits,须要的索引数是 2^32;
解决:
问题1
整数是由数字组成的。字符串是由字符组成的。
在整数的时候。索引取的是各个数字的十进制组合表示。 如 1234 取索引 1*10^3 + 2*10^2 + 3*10^1 + 4*10^0
在字符串的时候,索引相同也能够取各个字符的ASCII编码组合表示。如 hou 能够取索引 'h'*128^2 + 'o'*128^1 + 'u'*128^0
问题2
採用hash function 将元素值映射到大小可接受的索引范围
-->问题:不同元素被映射到同样的位置
-->解决:线性探測、二次探測、开链
线性、二次指的是碰撞时前进的步伐大小
线性 --> H+1, H+2, H+3, ...
二次 --> H+1^2, H+2^2, H+3^2, ...
开链法是指在每个表元素中维护一个 list ,在那个元素上运行元素的插入、搜寻、删除
SGI STL 的 hash table 採用的是开链法
hashtable 的能容纳的元素个数就是 bucket vector 的大小。当超过这个大小时。hashtable就会调用 resize() 函数又一次分配大小。
重建新的 hashtable
#ifndef __SGI_STL_INTERNAL_HASHTABLE_H
#define __SGI_STL_INTERNAL_HASHTABLE_H // Hashtable class, used to implement the hashed associative containers
// hash_set, hash_map, hash_multiset, and hash_multimap. #include <stl_algobase.h>
#include <stl_alloc.h>
#include <stl_construct.h>
#include <stl_tempbuf.h>
#include <stl_algo.h>
#include <stl_uninitialized.h>
#include <stl_function.h>
#include <stl_vector.h>
#include <stl_hash_fun.h> __STL_BEGIN_NAMESPACE // hashtable 元素所维护的链表节点
template <class Value>
struct __hashtable_node
{
__hashtable_node* next;
Value val;
}; template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc = alloc>
class hashtable; template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator; template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_const_iterator; //hashtable 的迭代器
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator {
typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>
hashtable;
typedef __hashtable_iterator<Value, Key, HashFcn,
ExtractKey, EqualKey, Alloc>
iterator;
typedef __hashtable_const_iterator<Value, Key, HashFcn,
ExtractKey, EqualKey, Alloc>
const_iterator;
typedef __hashtable_node<Value> node; typedef forward_iterator_tag iterator_category;
typedef Value value_type;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef Value& reference;
typedef Value* pointer; node* cur; //迭代器眼下所指的节点
hashtable* ht; //保持对容器的连结关系(由于可能须要从 bucket 跳到 bucket) __hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {}
__hashtable_iterator() {}
reference operator*() const { return cur->val; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
iterator& operator++();
iterator operator++(int);
bool operator==(const iterator& it) const { return cur == it.cur; }
bool operator!=(const iterator& it) const { return cur != it.cur; }
}; //为什么会有个专门的 __hashtable_const_iterator 。用 const __hashtable_iterator 不行吗?
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_const_iterator {
typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>
hashtable;
typedef __hashtable_iterator<Value, Key, HashFcn,
ExtractKey, EqualKey, Alloc>
iterator;
typedef __hashtable_const_iterator<Value, Key, HashFcn,
ExtractKey, EqualKey, Alloc>
const_iterator;
typedef __hashtable_node<Value> node; typedef forward_iterator_tag iterator_category;
typedef Value value_type;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef const Value& reference;
typedef const Value* pointer; const node* cur;
const hashtable* ht; __hashtable_const_iterator(const node* n, const hashtable* tab)
: cur(n), ht(tab) {}
__hashtable_const_iterator() {}
__hashtable_const_iterator(const iterator& it) : cur(it.cur), ht(it.ht) {}
reference operator*() const { return cur->val; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
const_iterator& operator++();
const_iterator operator++(int);
bool operator==(const const_iterator& it) const { return cur == it.cur; }
bool operator!=(const const_iterator& it) const { return cur != it.cur; }
}; //线性探測、二次探測的方法的表大小最好是质数
//开链法的表不须要是质数。只是 SGI STL 仍以质数来设计表的大小
// Note: assumes long is at least 32 bits.
static const int __stl_num_primes = 28;
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
53, 97, 193, 389, 769,
1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433,
1572869, 3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189, 805306457,
1610612741, 3221225473ul, 4294967291ul
}; inline unsigned long __stl_next_prime(unsigned long n)
{
const unsigned long* first = __stl_prime_list;
const unsigned long* last = __stl_prime_list + __stl_num_primes;
const unsigned long* pos = lower_bound(first, last, n); //用 lower_bound 来查找与要设计的表的大小最接近的质数
return pos == last ? *(last - 1) : *pos;
} template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey,
class Alloc>
class hashtable {
public:
typedef Key key_type; //键值的类型
typedef Value value_type; //实值的类型
typedef HashFcn hasher; //hash function
typedef EqualKey key_equal;//推断键值是否同样 typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference; hasher hash_funct() const { return hash; }
key_equal key_eq() const { return equals; } private:
// 三个 function object
hasher hash;
key_equal equals;
ExtractKey get_key; typedef __hashtable_node<Value> node;
typedef simple_alloc<node, Alloc> node_allocator; //每次分配一个 node 大小的空间 vector<node*,Alloc> buckets; // 以 vector 表示 buckets 数组,每个 bucket 元素维护一个指向链表的 node * 地址
size_type num_elements; //hashtable 里的元素个数 public:
typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey,
Alloc>
iterator; typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey,
Alloc>
const_iterator; friend struct
__hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
friend struct
__hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>; public:
hashtable(size_type n,
const HashFcn& hf,
const EqualKey& eql,
const ExtractKey& ext)
: hash(hf), equals(eql), get_key(ext), num_elements(0)
{
initialize_buckets(n);
} hashtable(size_type n,
const HashFcn& hf,
const EqualKey& eql)
: hash(hf), equals(eql), get_key(ExtractKey()), num_elements(0)
{
initialize_buckets(n);
} hashtable(const hashtable& ht)
: hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_elements(0)
{
copy_from(ht);
} hashtable& operator= (const hashtable& ht)
{
if (&ht != this) {
clear();
hash = ht.hash;
equals = ht.equals;
get_key = ht.get_key;
copy_from(ht);
}
return *this;
} ~hashtable() { clear(); } size_type size() const { return num_elements; }
size_type max_size() const { return size_type(-1); }
bool empty() const { return size() == 0; } void swap(hashtable& ht)
{
__STD::swap(hash, ht.hash);
__STD::swap(equals, ht.equals);
__STD::swap(get_key, ht.get_key);
buckets.swap(ht.buckets);
__STD::swap(num_elements, ht.num_elements);
} iterator begin()
{
for (size_type n = 0; n < buckets.size(); ++n)
if (buckets[n])
return iterator(buckets[n], this);
return end();
} iterator end() { return iterator(0, this); } const_iterator begin() const
{
for (size_type n = 0; n < buckets.size(); ++n)
if (buckets[n])
return const_iterator(buckets[n], this);
return end();
} const_iterator end() const { return const_iterator(0, this); } friend bool
operator== __STL_NULL_TMPL_ARGS (const hashtable&, const hashtable&); public: size_type bucket_count() const { return buckets.size(); } size_type max_bucket_count() const
{ return __stl_prime_list[__stl_num_primes - 1]; } size_type elems_in_bucket(size_type bucket) const
{
size_type result = 0;
for (node* cur = buckets[bucket]; cur; cur = cur->next)
result += 1;
return result;
} //插入元素,不许反复
pair<iterator, bool> insert_unique(const value_type& obj)
{
resize(num_elements + 1); //推断是否须要重建表,如须要就扩充
return insert_unique_noresize(obj);
} //插入元素。同意反复
iterator insert_equal(const value_type& obj)
{
resize(num_elements + 1);
return insert_equal_noresize(obj);
} pair<iterator, bool> insert_unique_noresize(const value_type& obj);
iterator insert_equal_noresize(const value_type& obj); #ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
void insert_unique(InputIterator f, InputIterator l)
{
insert_unique(f, l, iterator_category(f));
} template <class InputIterator>
void insert_equal(InputIterator f, InputIterator l)
{
insert_equal(f, l, iterator_category(f));
} template <class InputIterator>
void insert_unique(InputIterator f, InputIterator l,
input_iterator_tag)
{
for ( ; f != l; ++f)
insert_unique(*f);
} template <class InputIterator>
void insert_equal(InputIterator f, InputIterator l,
input_iterator_tag)
{
for ( ; f != l; ++f)
insert_equal(*f);
} template <class ForwardIterator>
void insert_unique(ForwardIterator f, ForwardIterator l,
forward_iterator_tag)
{
size_type n = 0;
distance(f, l, n);
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_unique_noresize(*f);
} template <class ForwardIterator>
void insert_equal(ForwardIterator f, ForwardIterator l,
forward_iterator_tag)
{
size_type n = 0;
distance(f, l, n);
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_equal_noresize(*f);
} #else /* __STL_MEMBER_TEMPLATES */
void insert_unique(const value_type* f, const value_type* l)
{
size_type n = l - f;
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_unique_noresize(*f);
} void insert_equal(const value_type* f, const value_type* l)
{
size_type n = l - f;
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_equal_noresize(*f);
} void insert_unique(const_iterator f, const_iterator l)
{
size_type n = 0;
distance(f, l, n);
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_unique_noresize(*f);
} void insert_equal(const_iterator f, const_iterator l)
{
size_type n = 0;
distance(f, l, n);
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_equal_noresize(*f);
}
#endif /*__STL_MEMBER_TEMPLATES */ reference find_or_insert(const value_type& obj); iterator find(const key_type& key)
{
//首先得到键值 key 应该落入的 bucket
size_type n = bkt_num_key(key);
node* first;
//然后遍历这个 bucket 看看能不能找到有同样 key 的元素
for ( first = buckets[n];
first && !equals(get_key(first->val), key);
first = first->next)
{}
return iterator(first, this);
} const_iterator find(const key_type& key) const
{
size_type n = bkt_num_key(key);
const node* first;
for ( first = buckets[n];
first && !equals(get_key(first->val), key);
first = first->next)
{}
return const_iterator(first, this);
} size_type count(const key_type& key) const
{
const size_type n = bkt_num_key(key);
size_type result = 0; for (const node* cur = buckets[n]; cur; cur = cur->next)
if (equals(get_key(cur->val), key))
++result;
return result;
} pair<iterator, iterator> equal_range(const key_type& key);
pair<const_iterator, const_iterator> equal_range(const key_type& key) const; size_type erase(const key_type& key);
void erase(const iterator& it);
void erase(iterator first, iterator last); void erase(const const_iterator& it);
void erase(const_iterator first, const_iterator last); void resize(size_type num_elements_hint);
void clear(); private:
size_type next_size(size_type n) const { return __stl_next_prime(n); } void initialize_buckets(size_type n)
{
const size_type n_buckets = next_size(n);
buckets.reserve(n_buckets);
buckets.insert(buckets.end(), n_buckets, (node*) 0);
num_elements = 0;
} // 接受键值。 返回 key 位于哪个 bucket
size_type bkt_num_key(const key_type& key) const
{
return bkt_num_key(key, buckets.size());
} //接受实值, 返回 obj 位于哪个 bucket
size_type bkt_num(const value_type& obj) const
{
// 调用 get_key 得到 key,调用 bkt_num_key 得到 #bucket
return bkt_num_key(get_key(obj));
}
// 接受键值和 bucket 个数, 返回 key 位于哪个 bucket
size_type bkt_num_key(const key_type& key, size_t n) const
{
return hash(key) % n;
}
//接受实值和 bucket 个数, 返回 obj 位于哪个 bucket
size_type bkt_num(const value_type& obj, size_t n) const
{
return bkt_num_key(get_key(obj), n);
} node* new_node(const value_type& obj)
{
node* n = node_allocator::allocate(); //分配节点空间
n->next = 0;
__STL_TRY {
construct(&n->val, obj); //构造节点
return n;
}
__STL_UNWIND(node_allocator::deallocate(n));
} void delete_node(node* n)
{
destroy(&n->val); //析构节点
node_allocator::deallocate(n); //释放空间
} void erase_bucket(const size_type n, node* first, node* last);
void erase_bucket(const size_type n, node* last); void copy_from(const hashtable& ht); }; template <class V, class K, class HF, class ExK, class EqK, class A>
__hashtable_iterator<V, K, HF, ExK, EqK, A>&
__hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++()
{
const node* old = cur;
cur = cur->next; //假设当前迭代器所指的链表节点有下一个节点,那就将迭代器指向那个节点。 否则就要跳到下一个 bucket 了
if (!cur) {
size_type bucket = ht->bkt_num(old->val); //定位当前 bucket 的下一个
while (!cur && ++bucket < ht->buckets.size()) //找到当前 bucket 之后的第一个不为空的 bucket
cur = ht->buckets[bucket]; //让 cur 指向 bucket 的头节点
}
return *this;
} //如非必要,还是使用前置 operator++ 吧。后置的编译器要帮它生成和个 int 參数,执行时要产生暂时对象
//内部还是调用前置 operator++ 实现的,返回的时候又要调用拷贝构造函数,还要析构掉之前生成的暂时对象
template <class V, class K, class HF, class ExK, class EqK, class A>
inline __hashtable_iterator<V, K, HF, ExK, EqK, A>
__hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++(int)
{
iterator tmp = *this;
++*this;
return tmp;
} template <class V, class K, class HF, class ExK, class EqK, class A>
__hashtable_const_iterator<V, K, HF, ExK, EqK, A>&
__hashtable_const_iterator<V, K, HF, ExK, EqK, A>::operator++()
{
const node* old = cur;
cur = cur->next;
if (!cur) {
size_type bucket = ht->bkt_num(old->val);
while (!cur && ++bucket < ht->buckets.size())
cur = ht->buckets[bucket];
}
return *this;
} template <class V, class K, class HF, class ExK, class EqK, class A>
inline __hashtable_const_iterator<V, K, HF, ExK, EqK, A>
__hashtable_const_iterator<V, K, HF, ExK, EqK, A>::operator++(int)
{
const_iterator tmp = *this;
++*this;
return tmp;
} #ifndef __STL_CLASS_PARTIAL_SPECIALIZATION template <class V, class K, class HF, class ExK, class EqK, class All>
inline forward_iterator_tag
iterator_category(const __hashtable_iterator<V, K, HF, ExK, EqK, All>&)
{
return forward_iterator_tag();
} template <class V, class K, class HF, class ExK, class EqK, class All>
inline V* value_type(const __hashtable_iterator<V, K, HF, ExK, EqK, All>&)
{
return (V*) 0;
} template <class V, class K, class HF, class ExK, class EqK, class All>
inline hashtable<V, K, HF, ExK, EqK, All>::difference_type*
distance_type(const __hashtable_iterator<V, K, HF, ExK, EqK, All>&)
{
return (hashtable<V, K, HF, ExK, EqK, All>::difference_type*) 0;
} template <class V, class K, class HF, class ExK, class EqK, class All>
inline forward_iterator_tag
iterator_category(const __hashtable_const_iterator<V, K, HF, ExK, EqK, All>&)
{
return forward_iterator_tag();
} template <class V, class K, class HF, class ExK, class EqK, class All>
inline V*
value_type(const __hashtable_const_iterator<V, K, HF, ExK, EqK, All>&)
{
return (V*) 0;
} template <class V, class K, class HF, class ExK, class EqK, class All>
inline hashtable<V, K, HF, ExK, EqK, All>::difference_type*
distance_type(const __hashtable_const_iterator<V, K, HF, ExK, EqK, All>&)
{
return (hashtable<V, K, HF, ExK, EqK, All>::difference_type*) 0;
} #endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ template <class V, class K, class HF, class Ex, class Eq, class A>
bool operator==(const hashtable<V, K, HF, Ex, Eq, A>& ht1,
const hashtable<V, K, HF, Ex, Eq, A>& ht2)
{
typedef typename hashtable<V, K, HF, Ex, Eq, A>::node node;
if (ht1.buckets.size() != ht2.buckets.size())
return false;
for (int n = 0; n < ht1.buckets.size(); ++n) {
node* cur1 = ht1.buckets[n];
node* cur2 = ht2.buckets[n];
for ( ; cur1 && cur2 && cur1->val == cur2->val;
cur1 = cur1->next, cur2 = cur2->next)
{}
if (cur1 || cur2)
return false;
}
return true;
} #ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER template <class Val, class Key, class HF, class Extract, class EqKey, class A>
inline void swap(hashtable<Val, Key, HF, Extract, EqKey, A>& ht1,
hashtable<Val, Key, HF, Extract, EqKey, A>& ht2) {
ht1.swap(ht2);
} #endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ //在不须要重建表的情况下插入新节点。键值反复的节点不会插入
template <class V, class K, class HF, class Ex, class Eq, class A>
pair<typename hashtable<V, K, HF, Ex, Eq, A>::iterator, bool>
hashtable<V, K, HF, Ex, Eq, A>::insert_unique_noresize(const value_type& obj)
{
const size_type n = bkt_num(obj); //决定 obj 就位于哪个 bucket
node* first = buckets[n]; //令 first 指向 bucket 相应的串行头部 for (node* cur = first; cur; cur = cur->next)
//发现键值与链表同的某节点键值同样,不插入
if (equals(get_key(cur->val), get_key(obj)))
return pair<iterator, bool>(iterator(cur, this), false); //在链表头插入新节点
node* tmp = new_node(obj);
tmp->next = first;
buckets[n] = tmp;
++num_elements;
return pair<iterator, bool>(iterator(tmp, this), true);
} //在不须要重建表的情况下插入新节点。插入的节点键值能够反复
template <class V, class K, class HF, class Ex, class Eq, class A>
typename hashtable<V, K, HF, Ex, Eq, A>::iterator
hashtable<V, K, HF, Ex, Eq, A>::insert_equal_noresize(const value_type& obj)
{
const size_type n = bkt_num(obj); //决定 obj 就位于哪个 bucket
node* first = buckets[n]; //令 first 指向 bucket 相应的串行头部 for (node* cur = first; cur; cur = cur->next)
//假设发现与链表中的某键值同样。就立即插入。然后返回
if (equals(get_key(cur->val), get_key(obj))) {
node* tmp = new_node(obj);
tmp->next = cur->next;
cur->next = tmp;
++num_elements;
return iterator(tmp, this);
}
//链表中没有同样键值的节点,将新节点插入在链表
node* tmp = new_node(obj);
tmp->next = first;
buckets[n] = tmp;
++num_elements;
return iterator(tmp, this);
} template <class V, class K, class HF, class Ex, class Eq, class A>
typename hashtable<V, K, HF, Ex, Eq, A>::reference
hashtable<V, K, HF, Ex, Eq, A>::find_or_insert(const value_type& obj)
{
resize(num_elements + 1); size_type n = bkt_num(obj);
node* first = buckets[n]; for (node* cur = first; cur; cur = cur->next)
if (equals(get_key(cur->val), get_key(obj)))
return cur->val; node* tmp = new_node(obj);
tmp->next = first;
buckets[n] = tmp;
++num_elements;
return tmp->val;
} template <class V, class K, class HF, class Ex, class Eq, class A>
pair<typename hashtable<V, K, HF, Ex, Eq, A>::iterator,
typename hashtable<V, K, HF, Ex, Eq, A>::iterator>
hashtable<V, K, HF, Ex, Eq, A>::equal_range(const key_type& key)
{
typedef pair<iterator, iterator> pii;
const size_type n = bkt_num_key(key); for (node* first = buckets[n]; first; first = first->next) {
if (equals(get_key(first->val), key)) {
for (node* cur = first->next; cur; cur = cur->next)
if (!equals(get_key(cur->val), key))
return pii(iterator(first, this), iterator(cur, this));
for (size_type m = n + 1; m < buckets.size(); ++m)
if (buckets[m])
return pii(iterator(first, this),
iterator(buckets[m], this));
return pii(iterator(first, this), end());
}
}
return pii(end(), end());
} template <class V, class K, class HF, class Ex, class Eq, class A>
pair<typename hashtable<V, K, HF, Ex, Eq, A>::const_iterator,
typename hashtable<V, K, HF, Ex, Eq, A>::const_iterator>
hashtable<V, K, HF, Ex, Eq, A>::equal_range(const key_type& key) const
{
typedef pair<const_iterator, const_iterator> pii;
const size_type n = bkt_num_key(key); for (const node* first = buckets[n] ; first; first = first->next) {
if (equals(get_key(first->val), key)) {
for (const node* cur = first->next; cur; cur = cur->next)
if (!equals(get_key(cur->val), key))
return pii(const_iterator(first, this),
const_iterator(cur, this));
for (size_type m = n + 1; m < buckets.size(); ++m)
if (buckets[m])
return pii(const_iterator(first, this),
const_iterator(buckets[m], this));
return pii(const_iterator(first, this), end());
}
}
return pii(end(), end());
} template <class V, class K, class HF, class Ex, class Eq, class A>
typename hashtable<V, K, HF, Ex, Eq, A>::size_type
hashtable<V, K, HF, Ex, Eq, A>::erase(const key_type& key)
{
const size_type n = bkt_num_key(key);
node* first = buckets[n];
size_type erased = 0; if (first) {
node* cur = first;
node* next = cur->next;
while (next) {
if (equals(get_key(next->val), key)) {
cur->next = next->next;
delete_node(next);
next = cur->next;
++erased;
--num_elements;
}
else {
cur = next;
next = cur->next;
}
}
if (equals(get_key(first->val), key)) {
buckets[n] = first->next;
delete_node(first);
++erased;
--num_elements;
}
}
return erased;
} template <class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::erase(const iterator& it)
{
if (node* const p = it.cur) {
const size_type n = bkt_num(p->val);
node* cur = buckets[n]; if (cur == p) {
buckets[n] = cur->next;
delete_node(cur);
--num_elements;
}
else {
node* next = cur->next;
while (next) {
if (next == p) {
cur->next = next->next;
delete_node(next);
--num_elements;
break;
}
else {
cur = next;
next = cur->next;
}
}
}
}
} template <class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::erase(iterator first, iterator last)
{
size_type f_bucket = first.cur ? bkt_num(first.cur->val) : buckets.size();
size_type l_bucket = last.cur ? bkt_num(last.cur->val) : buckets.size(); if (first.cur == last.cur)
return;
else if (f_bucket == l_bucket)
erase_bucket(f_bucket, first.cur, last.cur);
else {
erase_bucket(f_bucket, first.cur, 0);
for (size_type n = f_bucket + 1; n < l_bucket; ++n)
erase_bucket(n, 0);
if (l_bucket != buckets.size())
erase_bucket(l_bucket, last.cur);
}
} template <class V, class K, class HF, class Ex, class Eq, class A>
inline void
hashtable<V, K, HF, Ex, Eq, A>::erase(const_iterator first,
const_iterator last)
{
erase(iterator(const_cast<node*>(first.cur),
const_cast<hashtable*>(first.ht)),
iterator(const_cast<node*>(last.cur),
const_cast<hashtable*>(last.ht)));
} template <class V, class K, class HF, class Ex, class Eq, class A>
inline void
hashtable<V, K, HF, Ex, Eq, A>::erase(const const_iterator& it)
{
erase(iterator(const_cast<node*>(it.cur),
const_cast<hashtable*>(it.ht)));
} //推断是否须要重建表。如须要就扩充
//为什么要 resize 呢? 底层的 vector 不是会动态添加大小吗?
// --> ??由于表太大了, 装载率太大,减少了查找效率
template <class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::resize(size_type num_elements_hint)
{
const size_type old_n = buckets.size();
//假设元素个数大于 bucket vector 的大小,就重建表
if (num_elements_hint > old_n) {
//新表的大小
const size_type n = next_size(num_elements_hint);
if (n > old_n) {
//暂时的表,用来存放新建立的表,之后会和原来的表 swap。
vector<node*, A> tmp(n, (node*) 0);
__STL_TRY {
//遍历每个旧 bucket
for (size_type bucket = 0; bucket < old_n; ++bucket) {
node* first = buckets[bucket];
//遍历bucket 里的每个节点
while (first) {
size_type new_bucket = bkt_num(first->val, n); //当前节点应落在新 bucket vector 的哪一个 bucket 里
// 以下四个操作当前节点链接到新 bucket 下的链表前面
buckets[bucket] = first->next;
first->next = tmp[new_bucket];
tmp[new_bucket] = first;
first = buckets[bucket];
}
}
buckets.swap(tmp); //和原来的表交换,由编译器收回 tmp。 --> 为什么要 swap 。把 tmp 作为新的 bucket vector 不好吗?
--> ?? 这样重建前指向旧表的迭代器如今不至于指向一个不存在的地址 --> 但指向的内容会发生改变吗?
}
# ifdef __STL_USE_EXCEPTIONS
catch(...) {
for (size_type bucket = 0; bucket < tmp.size(); ++bucket) {
while (tmp[bucket]) {
node* next = tmp[bucket]->next;
delete_node(tmp[bucket]);
tmp[bucket] = next;
}
}
throw;
}
# endif /* __STL_USE_EXCEPTIONS */
}
}
} template <class V, class K, class HF, class Ex, class Eq, class A>
void node* first, node* last)
{
node* cur = buckets[n];
if (cur == first)
erase_bucket(n, last);
else {
node* next;
for (next = cur->next; next != first; cur = next, next = cur->next)
;
while (next) {
cur->next = ne= cur->next;
--num_elements;
}
}
} template <class V, class K, class HF, class Ex, class Eq, class A>
void
hashtable<V, K, HF, Ex, Eq, A>::erase_bucket(const size_type n, node* last)
{
node* cur = buckets[n];
while (cur != last) {
node* next = cur->next;
delete_node(cur);
cur = next;
buckets[n] = cur;
--num_elements;
}
} //总体删除
//hashtable 由两部分组成 bucket vector 和 每个 bucket 维护的 链表
//释放空间是仅仅须要释放动态生成的空间,即每个 bucket 维护的链表
//bucket vector 的空间当它离开它的作用域时由编译器收回
template <class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::clear()
{
//遍历每个 bucket
for (size_type i = 0; i < buckets.size(); ++i) {
node* cur = buckets[i];
//将 bucket list 中的每个节点删除掉
while (cur != 0) {
node* next = cur->next;
delete_node(cur);
cur = next;
}
buckets[i] = 0;
}
num_elements = 0;
} //假设 ht 就是 this 呢? 这里怎么没有自我复制检查?
template <class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::copy_from(const hashtable& ht)
{
//先清除自己的 bucket vector ,这操作是调用 vector::clear。造成全部元素为 0
buckets.clear();
//假设自己的空间大于对方,就什么都不做,否则 reserve() 函数会把 vector 的空间变得和 ht.buckets.size() 一样大
buckets.reserve(ht.buckets.size());
//将每个 bucket 初始化为 NULL
buckets.insert(buckets.end(), ht.buckets.size(), (node*) 0);
__STL_TRY {
遍历对方的每个 bucket
for(size_type i = 0; i < ht.buckets.size(); ++i){
//假设 bucket 的链表不空,就复制它的每个节点
if(const node* cur = ht.buckets[i]){
node* copy = new_node(cur->val);
buckets[i] = copy; for (node* next = cur->next; next; cur = next, next = cur->next) {
copy->next = new_node(next->val);
copy = copy->next;
}
}
}
num_elements = ht.num_elements;
}
__STL_UNWIND(clear());
} __STL_END_NAMESPACE #endif /* __SGI_STL_INTERNAL_HASHTABLE_H */ // Local Variables:
// mode:C++
// End:
STL源代码剖析 容器 stl_hashtable.h的更多相关文章
- STL源代码剖析 容器 stl_map.h
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie map ------------------------------------------ ...
- STL源代码剖析 容器 stl_list.h
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie list ----------------------------------------- ...
- STL源代码剖析 容器 stl_stack.h
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie stack ---------------------------------------- ...
- STL源代码剖析 容器 stl_deque.h
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie deque ---------------------------------------- ...
- STL源代码剖析 容器 stl_vector.h
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie vector --------------------------------------- ...
- 《STL源代码剖析》---stl_deque.h阅读笔记(2)
看完,<STL源代码剖析>---stl_deque.h阅读笔记(1)后.再看代码: G++ 2.91.57,cygnus\cygwin-b20\include\g++\stl_deque. ...
- 《STL源代码剖析》---stl_alloc.h阅读笔记
这一节是讲空间的配置与释放,但不涉及对象的构造和析构,仅仅是解说对象构造前空前的申请以及对象析构后空间怎么释放. SGI版本号的STL对空间的的申请和释放做了例如以下考虑: 1.向堆申请空间 2.考虑 ...
- 《STL源代码剖析》---stl_set.h阅读笔记
SET是STL中的标准容器,SET里面的元素会依据键值自己主动排序,它不像map那样拥有实值value和键值key的相应,set仅仅有实值.SET的底层实现时RB-tree,当插入到RB-tree中后 ...
- 《STL源代码剖析》---stl_hash_set.h阅读笔记
STL仅仅规定接口和复杂度,对于详细实现不作要求.set大多以红黑树实现,但STL在标准规格之外提供了一个所谓的hash_set,以hash table实现.hash_set的接口,hash_tabl ...
随机推荐
- JQuery - 留言之后,不重新加载数据,直接显示发表内容
留言板中,发表信息的时候,使用Ajax存储到后台数据库,如果存储成功,不重新加载数据库,直接显示发表内容. 代码: var Nicehng = ''; var kkimgpath = ''; var ...
- 进阶:案例五: Dynamic 创建 Business Graphic
效果图: step: 无需节点无需UI 1.添加属性 2.代码: method WDDOMODIFYVIEW . DATA:lr_graph TYPE REF TO cl_wd_business_gr ...
- 基于visual Studio2013解决面试题之0207单词翻转
题目
- linux配置本地tomcat应用80端口转发
场景: 本地部署tomcat到8080端口,并期望本地访问80端口来访问本地tomcat. 结论: 使用linux下的iptables工具实现端口转发功能. 具体为 现取得root权限 执行iptab ...
- 怎样取消shutdown关机命令?-shutdown命令的使用解析
机房上课,可恶电脑总是被管理员測试,不时地弹出这个关机提示.怎样取消这个关机命令呢?其有用 shutdown -a 就可以.以下来学习一下shutdown命令的使用: shutdown这个命令预计 ...
- 随想录(移动app下的生活)
[ 声明:版权全部,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 我算不上非常潮的人,使用移动app的时间也非常短.换成android手机也是近期一年的事情,可 ...
- 《C++语言基础》实践參考——数组作数据成员
返回:贺老师课程教学链接 [项目5 - 数组作数据成员]阅读教材P255例8.4.注意到类中的数据成员能够是数组.设计一个工资类(Salary),当中类的数据成员例如以下: class Salary ...
- ExtJS拖拽效果
ExtJS拖拽效果 <html> <head> <title>hello</title> <meta http-equiv="conte ...
- 使用Understand获取某个函数(方法)的静态度量指标
在之前的一篇日志中,我简单总结了调用Understand的Perl API的方法,这里再简单总结一些经验: 在SciTools\doc\manuals\pdf目录下的understand_api.pd ...
- Swift - 访问控制(private,internal,public)
在Swift语言中,访问修饰符有三种,分别为private,internal和public.同时,Swift对于访问权限的控制,不是基于类的,而是基于文件的.其区别如下: 1,private priv ...