stl源码剖析 详细学习笔记 hashtable
//---------------------------15/03/24----------------------------
//hashtable
{
/*
概述:
sgi采用的是开链法完成hashtable的,也就是用链表来存储映射到相同位置的元素。
*/
//node(节点)
template<class Value>
struct __hashtable_node
{
__hashtable_node* next;
Value val;
};
//bucket维护的linked list不是stl的list或slist,而是自行维护的,table则是用vector完成以便扩充
//迭代器
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;
__hashtable_iterator(node* n, hashtable* tab): cur(n), ht(tab){}
__hashtable_iterator(){}
reference
operator*() const {return cur->val;}
pointer
operator->() const {return &(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;}
};
//operator++()
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是当前节点,用->next取得下个节点,如果这个位置已经没有更多节点,cur就是null
cur = cur->next;
if(!cur)
{
//如果没有更多节点,使用val取得之前的cur在table中的位置
size_type bucket = ht->bkt_num(old->val);
//只要cur为null
并且 下一个位置小于最大值
就让cur为下一个位置的节点(如果存在就有值,不然为null)
while(!cur && ++bucket < ht->buckets.size())
cur = ht->buckets[bucket];
}
//如果++没有找到下一个元素(之后没有元素了),cur就为ht->buckets[buckets.size()-1]==null
//返回的iterator指向null,否则指向下一个节点
return *this;
}
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++(int)
{
iterator tmp = *this;
++*this;
return tmp;
}
//没有--操作
//class声明式
template<class Value,
class Key, class HashFcn,
class ExtractKey,
class EqualKey, class Alloc=alloc>
class hashtable;
//class定义
//Value:节点的实值类型 Key:节点的键值类型 HashFcn:hash function的函数类型
//ExtractKey:
从节点中取出键值的方法(可以使函数或仿函数) EqualKey:判断键值相同与否的方法(函数或仿函数)
template<class Value,
class Key, class HashFcn,
class ExtractKey,
class EqualKey, class Alloc>
class hashtable
{
public:
typedef HashFcn hasher;
typedef EqualKey key_equel;
typedef size_t size_type;
private:
hasher hash;
key_equel equals;
ExtractKey get_key;
typedef __hashtable_node<Value> node;
typedef simple_alloc<node, Alloc> node_allocator;
vector<node*, Alloc> buckets;
size_type num_elements;
public:
size_type bucket_count()
const {return buckets.size();}
};
//虽然开链法不要求表格大小必须为质数,但是sgi stl还是用质数大小来设计表格(这样碰撞的机会会变小)
//而且先把28个质数计算好,以备随时访问,同时提供一个函数来查询最适合的质数
static const
;
static const
unsigned long __stl_prime_list[__stl_num_primes]=
{
53,
97, 193...
4294967291ul
};
//获取大于n的下一个质数,不会超过last-1
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;
//使用全局算法lower_bound获取n的位置,把指针当迭代器来用
const unsigned
long* pos = lower_bound(first, last, n);
) : *pos;
}
size_type max_bucket_count()
const
{
];
}
//构造与内存管理
template<class Value,
class Key, class HashFcn,
class ExtractKey,
class EqualKey, class Alloc>
class hashtable
{
typedef simple_alloc<node, Alloc> node_allocator;
//创建和销毁节点,和以前类型
node* new_node(const value_type& obj)
{
node* n =node_allocator::allocate();
n->next =
;
__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);
}
//构造函数,设置好hash函数,判断相等的函数,取得键值(键值是用来比较是否相等的)的函数
//根据n设置好table的大小
hashtable(size_type n,
const HashFcn& hf,
const EqualKey& eql)
:hash(hf), equals(eql), get_key(ExtractKey()), num_elements()
{
initialize_buckets(n);
}
//initialize_buckets
void initialize_buckets(size_type n)
{
//找到合适的质数
const size_type n_buckets = next_size(n);
//保留 n_buckets大小的空间
buckets.reserve(n_buckets);
buckets.insert(buckets.end(), n_buckets, (node*));
num_elements =
;
}
//insert
pair<iterator,
bool> insert_unique(const value_type& obj)
{
resize(num_elements +
);
return insert_unique_noresize(obj);
}
iterator insert_equal(const value_type& obj)
{
resize(num_elements +
);
return insert_equal_noresize(obj);
}
//取得位置,首先要根据get_key函数
取得键值,只要针对非数值类型的变量
//比如字符串,string或者自定义类对象(对于这些我们要自己写好get_key函数传入模版参数)
//n表示取模的大小,最后要先调用hash函数,然后取模(默认就是table的大小)
size_type bkt_num(const value_type& obj, size_t n)
const
{
return bkt_num_key(get_key(obj), n);
}
//使用默认的取模大小
size_type bkt_num(const value_type& obj)
const
{
return bkt_num_key(get_key(obj));
}
//使用默认的取模大小
size_type bkt_num_key(const key_type& key)
const
{
return bkt_num_key(key, buckets.size());
}
//真正的取位置操作,根据传入的键值,先调用hash函数,然后取模
size_type bkt_num_key(const value_type& obj, size_t n)
const
{
return hash(key) % n;
}
//find
iterator find(const key_type& key)
{
//找到位置
size_type n = bkt_num_key(key);
node* first;
//在链表中查找是否存在
这个键值
for( first = buckets[n];
first && !equals(get_key(first->val),key);
first = first->next)
{}
//不存在this就是null,会返回false,否则就是true
return iterator(first,
this);
}
//count
size_type count(const key_type& key)
const
{
//找到键值位置
const size_type n =bkt_num_key(key);
size_type result =
;
//找到链表中和键值相等的元素个数
for(const node* cur = buckets[n]; cur; cur = cur->next)
if(equals(get_key(cur->val), key))
++result;
return result;
}
};
//resize
template<class V,
class K, class HF,
class Ex, class Eq,
class A>
__hashtable_iterator<V, K, HF, Ex, Eq, A>&
__hashtable_iterator<V, K, HF, Ex, Eq, A>::resize(size_type num_elements_hint)
{
//纪录原来table的大小
const size_type old_n = buckets.size();
if(num_elements_hint > old_n)
{
//如果新大小大于原来table大小
//找到下一个合适的质数
const size_type n = next_size(num_elements_hint);
if(n > old_n)
{
//这里如果之前n就是最后一个质数了,就不会进入这个块语句了
//初始化n大小的为0的vector
vector<node*, A> tmp(n, (node*));
; bucket < old_n; ++bucket)
{
//从原来的table中一个个复制到新table中
node* first = buckets[bucket];
while(first)
{
size_type new_bucket = bkt_num(first->val, n);
buckets[bucket] = first->next;
first->next = tmp[new_bucket];
tmp[new_bucket] = first;
first = buckets[bucket];
}
}
//交换新的table为当前table
buckets.swap(tmp);
}
}
}
//insert_unique_noresize
template<class V,
class K, class HF,
class Ex, class Eq,
class A>
__hashtable_iterator<V, K, HF, Ex, Eq, A>&
__hashtable_iterator<V, K, HF, Ex, Eq, A>::insert_unique_noresize(const value_type& obj)
{
//插入操作不可重复
//找到位置
const size_type n =bkt_num(obj);
//第一个节点
node* first = buckets[n];
//查找节点,如果有重复的键值,就返回false
for(node* cur = first; cir; 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);
}
//insert_equal_noresize
template<class V,
class K, class HF,
class Ex, class Eq,
class A>
__hashtable_iterator<V, K, HF, Ex, Eq, A>&
__hashtable_iterator<V, K, HF, Ex, Eq, A>::insert_equal_noresize(const value_type& obj)
{
//可以插入相同键值的节点,如果有相同键值就插到原来键值的后面,否则成为第一个节点
const 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)))
{
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);
}
//clear
template<class V,
class K, class HF,
class Ex, class Eq,
class A>
__hashtable_iterator<V, K, HF, Ex, Eq, A>&
__hashtable_iterator<V, K, HF, Ex, Eq, A>::clear()
{
//从头部一个个删除节点
; i < buckets.size(); ++i)
{
node* cur = buckets[i];
)
{
node* next = cur->next;
delete_node(cur);
cur = next;
}
buckets[i] =
;
}
num_elements =
;
}
//copy_from
template<class V,
class K, class HF,
class Ex, class Eq,
class A>
__hashtable_iterator<V, K, HF, Ex, Eq, A>&
__hashtable_iterator<V, K, HF, Ex, Eq, A>::copy_from(const hashtable& ht)
{
//调用buckets.clear()
先清除原来的table,再插入要copy的各个节点
//疑惑:
为什么不先调用一次clear(),不这样
原来table中的节点不是都没释放么?
//不是会造成内存泄漏?
buckets.clear();
buckets.reserve(ht.buckets.size());
bucket.insert(buckets.end(), ht.buckets.size(), (node*)
);
__STL_TRY
{
; i < ht.buckets.size(); ++i)
{
if(const node* cur = ht.buckets[i])
{
node* copy = new_node(cur->val);
bucket[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());
}
template<class Key>
struct hash{};
//计算key值函数,计算char*类型的 key值:
h(n)=h(n-1)*5 + *s *s就是(*s)的ascii码值
//
这样计算就使得字符与其位置有关,如果少了h(n-1)*5 那么 asd
和dsa 就会计算得到相同的位置
inline size_t __stl_hash_string(const
char* s)
{
unsigned long h =
;
for( ; *s; ++s)
h =
*h + *s;
return size_t(h);
}
//模版特化
//仿函数:利用 __stl_hash_string
取得key值
__STL_TEMPLATE_NULL
struct hash<char*>
{
size_t
operator()(const
char* s) const {return __stl_hash_string(s);}
};
/*
总结:hashtable就是一个大小为质数的vector中存放一条链表,查找和存放都要利用hash函数取得
对应的位置,然后存放到链表中,hash函数可以自定义(自己针对自己的类型进行特化),hashtable的hash
函数正确来说其实就是取模操作(默认操作),上面说的hash函数只是给出一个键值(当然也可以在这里自定义hash函数,
比如加个质数什么的),最后当然还是要取模运算。
最后就是copy_from里为什么没有调用clear(),这样应该会造成内存泄漏,当然很可能是我想错了,毕竟这是大牛
们写的
我也查过最新的sgi stl 上面还是这么写的。
*/
}
stl源码剖析 详细学习笔记 hashtable的更多相关文章
- stl源码剖析 详细学习笔记 set map
// // set map.cpp // 笔记 // // Created by fam on 15/3/23. // // //---------------------------15/03 ...
- stl源码剖析 详细学习笔记 RB_tree (1)
// // RB_tree_STL.cpp // 笔记 // // Created by fam on 15/3/21. // // #include "RB_tree_STL.h&q ...
- stl源码剖析 详细学习笔记heap
// // heap.cpp // 笔记 // // Created by fam on 15/3/15. // // //---------------------------15/03/15 ...
- stl源码剖析 详细学习笔记 空间配置器
//---------------------------15/04/05---------------------------- /* 空间配置器概述: 1:new操作包含两个阶段操作 1>调 ...
- stl源码剖析 详细学习笔记 算法(1)
//---------------------------15/03/27---------------------------- //算法 { /* 质变算法:会改变操作对象之值 所有的stl算法都 ...
- stl源码剖析 详细学习笔记 算法总览
//****************************基本算法***************************** /* stl算法总览,不在stl标准规格的sgi专属算法,都以 *加以标 ...
- stl源码剖析 详细学习笔记 hashset hashmap
//---------------------------15/03/26---------------------------- //hash_set { /* hash_set概述: 1:这是一个 ...
- stl源码剖析 详细学习笔记 RB_tree (2)
//---------------------------15/03/22---------------------------- //一直好奇KeyOfValue是什么,查了下就是一个和仿函数差不多 ...
- stl源码剖析 详细学习笔记priority_queue slist
// // priority_queue.cpp // 笔记 // // Created by fam on 15/3/16. // // //------------------------- ...
随机推荐
- 固态硬盘和机械硬盘的比较和SQLSERVER在两种硬盘上的性能差异
固态硬盘和机械硬盘的比较和SQLSERVER在两种硬盘上的性能差异 在看这篇文章之前可以先看一下下面的文章: SSD小白用户收货!SSD的误区如何解决 这样配会损失性能?实测6种特殊装机方式 听说固态 ...
- winform调用jar包
因为工作需要,需要做一个数据上传的程序,客户规定的是:数据接口采用http连接,采用JSON-RPC轻量级远程调用协议.所以决定用winform做一个管理界面(其中还包括其他的功能),java完成数据 ...
- Sql Server中的游标最好只用于有主键或唯一键的表
游标cursor,我想大多数人都在sql server里面用过.当一个表数据量不太大的时候,游标还是可以用的,毕竟游标是循环一个表中每一行数据的最简便办法.但是如果你用一个游标去循环一个没有主键或唯一 ...
- Linux开启和关闭防火墙
一.即时生效,重启后失效: 1.启动:service iptables start 2.关闭:service iptables stop 二.重启后永久生效: 1.启动:chkconfig iptab ...
- MySQL锁(行锁、表锁、页锁、乐观锁、悲观锁等)
锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一 ...
- Linux uptime命令详解
常见的命令展示 uptime 08:21:34 up 36 min, 2 users, load average: 0.00, 0.00, 0.00 #当前服务器时间: 08:21:34 #当前服务器 ...
- WinThruster清理电脑注册表
电脑因为安装卸载各种软件,长时间工作,越来越卡慢,很大程度上和电脑中一些老旧不用的注册表有关,一些遗留问题也成为隐患. 今天我们主要来讲一下一些不用,没用的注册表清理问题. 无意间在网上看了一个软件, ...
- aspnet_regiis.exe -i 报 “此操作系统版本不支持此选项”
解决方法: 控制面板 - 程序和功能 - 启动或关闭windows功能 - Internet Information services - 万维网服务 - 应用程序开发功能 勾选: 1.ASP.NET ...
- Codeforces Round #503 Div. 2
时间相对来说还是比较合适的,正好放假就可以打一打啦. A. New Building for SIS:http://codeforces.com/contest/1020/problem/A 题意概述 ...
- 哪些地方会出现css阻塞,哪些地方会出现js阻塞?
Js的阻塞特性: 所有浏览器在下载JS的时候,会阻止一切其他活动,比如其他资源的下载,内容的呈现等等.直到JS下载.解析.执行完毕后才开始继续并行下载其他资源并呈现内容.为了提高用户体验,新一代浏览器 ...