三谈GetHashCode函数到Dictionary(或hash_set ,hash_map)

10 篇文章 0 订阅
3 篇文章 0 订阅


三谈GetHashCode函数到Dictionary(或hash_set ,hash_map)

    上一篇文章中再谈了GetHashCode函数的应用注意事项,以及如何应用。最近回家在翻看STL源码剖析的时候,又了解到了hsah_map,以及hash_set(C++中的,C#中是Dictionary)底层实现机制。正好又更深入的了解到了,为什么object中要有这个函数!

     那么本文就是来解释为什么Object中要有这个函数?

   

     1.hashTable的底层实现机制:

数据结构中描述的hashTable,我们都很清楚那几种解决冲突方式,如线性探测,二次探测,还有链地址法,而STL中实现是采用链地址法的,同时存放数据的bucket(底层是个vector)(下图所示),会动态增长,并不是一个固定的长度。


至于怎么增长,这里简单的描述一下,首先,这个vector的长度,可用28个质数来表示,并且第一个质数是53,然后大约两倍的关系递增。为了计算方便,stl中用一个28个元素的数组来保存这个vector的动态增长的长度。

如:

static const unsigned long _stl_prime_list[_stl_num_primes] = {

53,97,193,389,769.......................

}

   当调用hashTable的时候,传递要放入的数据个数n,hashTable便自动扩展至,距离这个n最近的一个质数大小的vector。譬如放入50,其实底部vector的大小是53,如果继续放入元素,超过53达到54的时候,那么vector的大小就会扩展至第二个质数97.


下面就是我们本文的重点:hashFunction(hash函数)


说了那么多,一定想知道,想必很多人也一定对STL的hash函数很感兴趣,到底是有多么高深的设计呢?大神的hash函数会是什么样的?


看了源码之后,会让人没有什么惊喜。。。。。

原来是那么简单。并且hash函数只能处理整型以及Const char*型,不能处理string,和浮点型的。

对于const char*和int型

<span style="font-size:24px;">//const char返回的计算每个字符的一个叠加和的整数值!
inline size_t _stl_hash_string(const char *s)
{
	unsigned long h = 0;
	for(;*s; ++s)
	{
		h = 5*h +*s;
		return size_t(h);

	}
}
//int值直接返回自身
_STL_TEMPLATE_NULL struct hash<int>{
	size_t operator(int x)const {return x;}
};</span>

所以从上面来看,这个hash函数,都是只能处理int型的值,然后直接返回一个int值,取模放在对应的位置上。

2.引出GetHashCode的实现:

从1中可以看到,Hash函数处理的时候,都是以整型来搞的,并且STL中是不支持对string进行映射的!

那么java中,C#中的string,和其他的object想要放入HashTable中该怎么办,该怎么写这个Hash函数呢?

看到这里我想很多人应该更深刻的了解了,这个GetHashCode函数的来历,以及明白C#中的值类型为什么不需要这个函数?(你明白了么?)

因为值类型,他的Hash值取的就是本身的值,底层有对应的这个Hash函数去做映射,底层已经提供这个Hash函数,并且能够处理,所以就不需要程序员来额外提供一个GetHashCode函数来自己实现一个Hash函数了。而对于引用值,底层并不能处理,譬如从上面看到底层的HashTable非常亲睐整型,所以当你给他个string,或者object时,他会感觉到莫名其妙!因为底层并没有针对这种类型的Hash函数,也就是说,string以及其他的object必须程序员自己提供一个Hash函数(其实就是这个GetHashCode函数!)

而这个函数在object中,msdn中的说法是,返回对象的引用值,也就是说白了,就是堆的指针值!

第二个问题也就接踵而来,我们之前强调的,为什么Equals和GetHashCode函数要同时存在呢?

因为如果子类中不重写GetHashCode函数,那么会默认调用基类Object中实现,他是返回该对象的引用值,那么想象一下这种情况,两个对象中的数据完全一样,但是是同个两个new new出来的,也就是说,他们分别在不同的堆上存放,但存放的数据完全一样,只是对象的引用(堆指针)不一样,那么我们把这两个对象放入Dictionary时,它会把它当做两个不同的对象来处理,显然很多时候这不是我们想要的结果(特别的时候也有可能需要!)。所以我们要自己实现一个GetHashCode函数来保证两个对象相等,那么他们的GetHashCode函数值也相等,而不是默认调用object中的GetHashCode函数,返回对象的引用值来作为Hash映射的值!







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值