**
Hash表、Hash函数和HashCode
**
-
Hash函数和Hash表:
Hash函数就是根据key计算出应该存储的位置,而Hash表则是基于Hash函数建立的一种查找表。给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
-
Hash常用的构造方法:
-
直接寻址法:取关键字或关键字的某个线性线性函数值为散列地址。即H(key)=a*key+b这种散列函数也称为自身函数。(a和b为常数)
-
数字分析法:H(key)=key%用有规律(差别大)的连续数据位数。
-
平方取中法:关键字的每一位都有某些数字重复出现频率很高的现象,可以先求关键字(key)的平方值,通过平方扩大差异,而后取中间数位(三位)作为最终存储地址。
-
折叠法:将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(去除进位)作为散列地址。
-
HashCode:
HashCode是object的一个方法,hashcode方法返回一个hashcode值,且这个方法是为了更好的支持hash表,比如String,Set,HashTable,HashMap等。Hashcode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。 -
HashCode性质
-
在一个Java应用的执行期间,如果一个对象提供给equals作比较的信息没有被修改的话,该对象多次调用hashcode()方法,该方法必须始终如一返回同一个Integer。
-
如果两个对象根据equals(object)方法是相等的,那么调用二者各自的hashcode()方法必须产生同一个Integer结果。
-
并不要求根据equals(java.lang.object)方法不相等的两个对象,调用二者各自的hashcode()方法必须产生不同的Integer的结果。然而,程序员应该意识到对于不同的对象产生不同的Integer结果,有可能会提高hashtable的性能。
-
HashCode小结
-
由object类定义的hashcode()方法对于不同的对象返回不同的Integer。
-
在object类中,hashcode定义如下:public native int hashcode();
-
了解hashcode的作用,要先知道java中的集合。
Java中的集合(collection)有两类,一类是List,一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不重复,可两个元素是否重复应该依据什么来判断?
这就是object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。
于是,Java采用了哈希表的原理。哈希(hash)实际上是人名,由于他提出了哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。
当集合要添加新的元素时,先调用这个元素的hashcode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用他的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址(或以链表的形式存储)。所以这里存在一个冲突解决的问题。这样实际调用equals方法的次数就大大降低了。
简而言之,在集合查找时,hashcode能大大降低对象比较次数,提高查找效率。 -
Java对象的equals方法和hashcode方法是这样规定的:
- 相等(相同)的对象必须具有相等的哈希码(或者散列码)。(解释:两个Java对象A和B,A和B相等(equals结果为true),但A和B的哈希码不同,则A和B存入hashmap时的哈希码计算得到的hashmap内部数组位置索引可能不同,那么A和B很有可能允许同时存入hashmap,显然相等/相同的元素是不允许同时存入hashmap,hashmap不允许存放重复元素。)
- 如果两个对象的hashcode相同,它们并不一定相同。(解释:不同对象的hashcode可能相同;两个Java对象A和B,A和B不相等(equals结果为false),但A和B的哈希码相等,将A和B都存入HashMap时会发生哈希冲突,也就是A和B存放在HashMap内部数组的位置索引相同时HashMap会在该位置建立一个链接表,将A和B串起来放在该位置,显然,该情况不违反HashMap的使用原则,是允许的。当然,哈希冲突越少越好,尽量采用好的哈希算法以避免哈希冲突。)
- 小结
- 如果两个对象相同,那么它们的hashcode值一定要相同;
- 如果两个对象的hashcode相同,它们并不一定相同(equals比较)。否则可能出现相同的对象可以出现在set集合中,同时,增加新元素的效率会降低。
- equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明它们的hashcode()不相等。
在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当前hashcode()也就相等了;在String类型,equals()返回的是两个对象内容的比较,当两个对象内容相等时,hashcode()方法根据String类的重写代码的分析,也可知到hashcode()返回结果也会相等。以此类推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。
参考:
https://www.jianshu.com/p/6eb3e2762277
https://baike.baidu.com/item/哈希表/5981869
https://www.cnblogs.com/Qian123/p/5703507.html#_label1