Hashcode用法.docx

上传人:b****5 文档编号:30727214 上传时间:2023-08-19 格式:DOCX 页数:16 大小:23.59KB
下载 相关 举报
Hashcode用法.docx_第1页
第1页 / 共16页
Hashcode用法.docx_第2页
第2页 / 共16页
Hashcode用法.docx_第3页
第3页 / 共16页
Hashcode用法.docx_第4页
第4页 / 共16页
Hashcode用法.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

Hashcode用法.docx

《Hashcode用法.docx》由会员分享,可在线阅读,更多相关《Hashcode用法.docx(16页珍藏版)》请在冰豆网上搜索。

Hashcode用法.docx

Hashcode用法

Hashcode()用法

1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有

例如内存中有这样的位置

01234567

而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。

但如果用hashcode那就会使效率提高很多。

我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。

比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。

这样,以后在查找该类时就可以通过ID除8求余数直接找到存放的位置了。

2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:

可以这样。

那么如何判断呢?

在这个时候就需要定义equals了。

也就是说,我们先通过hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过equals来在这个桶里找到我们要的类。

那么。

重写了equals(),为什么还要重写hashCode()呢?

想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊

3。

你要对A类排序,有两种方法,一种就是让A类实现comparabole结构并实现compareTo()方法,那么可以通过Collections.sort(Listlist)对其进行排序

另一种方法:

自己定义一个类B实现Comparator类并实现compare方法,

然后通过Collections.sort(Listlist,Bb)进行排序

hashCode()是用来产生哈希玛的,而哈希玛是用来在散列存储结构中确定对象的存储地址的,(这一段在Java编程思想中讲的很清楚的)象util包中的带hash的集合类都是用这种存储结构:

HashMap,HashSet,他们在将对象存储时(严格说是对象引用),需要确定他们的地址吧,而HashCode()就是这个用途的,一般都需要重新定义它的,因为默认情况下,由Object类定义的hashCode方法会针对不同的对象返回不同的整数,这一般是通过将该对象的内部地址转换成一个整数来实现的,现在举个例子来说,就拿HashSet来说,在将对象存入其中时,通过被存入对象的hashCode()来确定对象在HashSet中的存储地址,通过equals()来确定存入的对象是否重复,hashCode(),equals()都需要自己重新定义,因为hashCode()默认前面已经说啦,而equals()默认是比较的对象引用,你现在想一下,如果你不定义equals()的话,那么同一个类产生的两个内容完全相同的对象都可以存入Set,因为他们是通过equals()来确定的,这样就使得HashSet失去了他的意义,看一下下面这个:

publicclassTest{

publicstaticvoidmain(String[]args){

HashSetset=newHashSet();

for(inti=0;i<=3;i++){

set.add(newDemo1(i,i));

}

System.out.println(set);

set.add(newDemo1(1,1));

System.out.println(set);

System.out.println(set.contains(newDemo1(0,0)));

System.out.println(set.add(newDemo1(1,1)));

System.out.println(set.add(newDemo1(4,4)));

System.out.println(set);

}

 

privatestaticclassDemo1{

privateintvalue;

 

privateintid;

 

publicDemo1(intvalue,intid){

this.value=value;

this.id=id;

}

 

publicStringtoString(){

return"value="+value;

}

 

publicbooleanequals(Objecto){

Demo1a=(Demo1)o;

return(a.value==value)?

true:

false;

}

 

publicinthashCode(){

returnid;

}

}

}

你分别注释掉hashCode()和equals()来比较一下他们作用就可以拉,关键要自己动手看看比较的结果你就可以记得很清楚啦

如果还不是很明确可以再看另一个例子:

 

publicfinalclassTest{

 

publicstaticvoidmain(String[]args){

Mapm=newHashMap();

m.put(newPhoneNumber(020,12345678),"shellfeng");

System.out.println(m.get(newPhoneNumber(020,12345678)));

}

 

privatestaticclassPhoneNumber{

/**

*区号

*/

privateshortareaCode;

 

/**

*扩展号

*/

privateshortextension;

 

publicPhoneNumber(intareaCode,intextension){

this.areaCode=(short)areaCode;

this.extension=(short)extension;

}

 

publicbooleanequals(Objecto){

if(o==this){

returntrue;

}

if(!

(oinstanceofPhoneNumber)){

returnfalse;

}

PhoneNumberpn=(PhoneNumber)o;

returnpn.extension==extension&&pn.areaCode==areaCode;

}

 

/**

*@seejava.lang.Object#hashCode()

*@returnresult就是我们得到的散列值,其实我们的计算过程可以多种,这里只不过是一个例子,需要你的灵活运用,使其接近你需要的理想结果

*/

publicinthashCode(){

intresult=17;

result=37*result+areaCode;

result=37*result+extension;

returnresult;

}

}

}

还是那句话:

你注释掉hashCode()比较一下他们作用就可以拉,关键要自己动手看看比较的结果你就可以记得很清楚啦

总结

hashCode()方法使用来提高Map里面的搜索效率的,Map会根据不同的hashCode()来放在不同的桶里面,Map在搜索一个对象的时候先通过hashCode()找到相应的桶,然后再根据equals()方法找到相应的对象.要正确的实现Map里面查找元素必须满足一下两个条件:

(1)当obj1.equals(obj2)为true时obj1.hashCode()==obj2.hashCode()必须为true

(2)当obj1.hashCode()==obj2.hashCode()为false时obj.equals(obj2)必须为false

Java中的集合(Collection)有两类,一类是List,再有一类是Set。

你知道它们的区别吗?

前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。

那么这里就有一个比较严重的问题了:

要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?

这就是Object.equals方法了。

但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。

也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。

这显然会大大降低效率。

哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。

我们可以认为hashCode方法返回的就是对象存储的物理地址(实际可能并不是,例如:

通过获取对象的物理地址然后除以8再求余,余数几是计算得到的散列值,我们就认为返回一个不是物理地址的数值,而是一个可以映射到物理地址的值)。

这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。

如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。

所以这里存在一个冲突解决的问题。

这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

 

改写equals时总是要改写hashCode

============================================================

java.lnag.Object中对hashCode的约定:

1.在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。

2.如果两个对象根据equals(Objecto)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。

3.如果两个对象根据equals(Objecto)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。

但如果能不同,则可能提高散列表的性能。

 

有一个概念要牢记,两个相等对象的equals方法一定为true,但两个hashcode相等的对象不一定是相等的对象。

所以hashcode相等只能保证两个对象在一个HASH表里的同一条HASH链上,继而通过equals方法才能确定是不是同一对象,如果结果为true,则认为是同一对象不在插入,否则认为是不同对象继续插入。

Object的代码:

publicStringtoString(){

returnthis.getClass().getName()+"@"+Integer.toHexString(this.hashCode());

}

publicbooleanequals(Objecto){

returnthis==o;

}

/**

*Answersanintegerhashcodeforthereceiver.Anytwo

*objectswhichanswertruewhenpassedto

*.equalsmustanswerthesamevalueforthis

*method.

*

*@authorOTI

*@versioninitial

*

*@returnint

*thereceiver'shash.

*

*@see#equals

*/

publicnativeinthashCode();

 

从上面我们可以看到是否很可能Object.hashCode就是代表内存地址。

下面我们来证明hashcode是不是真的就是Object的内存地址呢?

实际上,hashcode根本不能代表object的内存地址。

-----------------------------------------

Object.hashCode不可以代表内存地址

----------------------------------------

packagecom.tools;

importjava.util.ArrayList;

/**

*此方法的作用是证明java.lang.Object的hashcode不是代表对象所在内存地址。

*我产生了10000个对象,这10000个对象在内存中是不同的地址,但是实际上这10000个对象

*的hashcode的是完全可能相同的

*/

publicclassHashCodeMeaning{

publicstaticvoidmain(String[]args){

ArrayListlist=newArrayList();

intnumberExist=0;

//证明hashcode的值不是内存地址

for(inti=0;i<10000;i++){

Objectobj=newObject();

if(list.contains(obj.toString())){

System.out.println(obj.toString()+"existsinthelist."+i);

numberExist++;

}

else{

list.add(obj.toString());

}

}

System.out.println("repetitionnumber:

"+numberExist);

System.out.println("listsize:

"+list.size());

//证明内存地址是不同的。

numberExist=0;

list.clear();

for(inti=0;i<10000;i++){

Objectobj=newObject();

if(list.contains(obj)){

System.out.println(obj+"existsinthelist."+i);

numberExist++;

}

else{

list.add(obj);

}

}

System.out.println("repetitionnumber:

"+numberExist);

System.out.println("listsize:

"+list.size());

}

}

 

==============================

看HashTable的源代码非常有用:

==============================

 

============================================================

有效和正确定义hashCode()和equals():

============================================================

级别:

入门级

 

每个Java对象都有hashCode()和equals()方法。

许多类忽略(Override)这些方法的缺省实施,以在对象实例之间提供更深层次的语义可比性。

在Java理念和实践这一部分,Java开发人员BrianGoetz向您介绍在创建Java类以有效和准确定义hashCode()和equals()时应遵循的规则和指南。

您可以在讨论论坛与作者和其它读者一同探讨您对本文的看法。

(您还可以点击本文顶部或底部的讨论进入论坛。

虽然Java语言不直接支持关联数组--可以使用任何对象作为一个索引的数组--但在根Object类中使用hashCode()方法明确表示期望广泛使用HashMap(及其前辈Hashtable)。

理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式中支持散列可以促进基于散列的容器的开发和使用。

定义对象的相等性

Object类有两种方法来推断对象的标识:

equals()和hashCode()。

一般来说,如果您忽略了其中一种,您必须同时忽略这两种,因为两者之间有必须维持的至关重要的关系。

特殊情况是根据equals()方法,如果两个对象是相等的,它们必须有相同的hashCode()值(尽管这通常不是真的)。

特定类的equals()的语义在Implementer的左侧定义;定义对特定类来说equals()意味着什么是其设计工作的一部分。

Object提供的缺省实施简单引用下面等式:

publicbooleanequals(Objectobj){return(this==obj);}

在这种缺省实施情况下,只有它们引用真正同一个对象时这两个引用才是相等的。

同样,Object提供的hashCode()的缺省实施通过将对象的内存地址对映于一个整数值来生成。

由于在某些架构上,地址空间大于int值的范围,两个不同的对象有相同的hashCode()是可能的。

如果您忽略了hashCode(),您仍旧可以使用System.identityHashCode()方法来接入这类缺省值。

忽略equals()--简单实例

缺省情况下,equals()和hashCode()基于标识的实施是合理的,但对于某些类来说,它们希望放宽等式的定义。

例如,Integer类定义equals()与下面类似:

publicbooleanequals(Objectobj){

return(objinstanceofInteger

&&intValue()==((Integer)obj).intValue());

}

在这个定义中,只有在包含相同的整数值的情况下这两个Integer对象是相等的。

结合将不可修改的Integer,这使得使用Integer作为HashMap中的关键字是切实可行的。

这种基于值的Equal方法可以由Java类库中的所有原始封装类使用,如Integer、Float、Character和Boolean以及String(如果两个String对象包含相同顺序的字符,那它们是相等的)。

由于这些类都是不可修改的并且可以实施hashCode()和equals(),它们都可以做为很好的散列关键字。

为什么忽略equals()和hashCode()?

如果Integer不忽略equals()和hashCode()情况又将如何?

如果我们从未在HashMap或其它基于散列的集合中使用Integer作为关键字的话,什么也不会发生。

但是,如果我们在HashMap中使用这类Integer对象作为关键字,我们将不能够可靠地检索相关的值,除非我们在get()调用中使用与put()调用中极其类似的Integer实例。

这要求确保在我们的整个程序中,只能使用对应于特定整数值的Integer对象的一个实例。

不用说,这种方法极不方便而且错误频频。

Object的interfacecontract要求如果根据equals()两个对象是相等的,那么它们必须有相同的hashCode()值。

当其识别能力整个包含在equals()中时,为什么我们的根对象类需要hashCode()?

hashCode()方法纯粹用于提高效率。

Java平台设计人员预计到了典型Java应用程序中基于散列的集合类(CollectionClass)的重要性--如Hashtable、HashMap和HashSet,并且使用equals()与许多对象进行比较在计算方面非常昂贵。

使所有Java对象都能够支持hashCode()并结合使用基于散列的集合,可以实现有效的存储和检索。

==============================

GodeepintoHashCode:

==============================

为什么HashCode对于对象是如此的重要?

一个对象的HashCode就是一个简单的Hash算法的实现,虽然它和那些真正的复杂的

Hash算法相比还不能叫真正的算法,但如何实现它,不仅仅是程序员的编程水平问题,

而是关系到你的对象在存取时性能的非常重要的问题.有可能,不同的HashCode可能

会使你的对象存取产生,成百上千倍的性能差别.

我们先来看一下,在JAVA中两个重要的数据结构:

HashMap和Hashtable,虽然它们有很

大的区别,如继承关系不同,对value的约束条件(是否允许null)不同,以及线程安全性

等有着特定的区别,但从实现原理上来说,它们是一致的.所以,我们只以Hashtable来

说明:

在java中,存取数据的性能,一般来说当然是首推数组,但是在数据量稍大的容器选择中,

Hashtable将有比数据性能更高的查询速度.具体原因看下面的内容.

Hashtable在存储数据时,一般先将该对象的HashCode和0x7FFFFFFF做与操作,因为一个

对象的HashCode可以为负数,这样操作后可以保证它为一个正整数.然后以Hashtable的

长度取模,得到该对象在Hashtable中的索引.

index=(o.hashCode()&0x7FFFFFFF)%hs.length;

这个对象就会直接放在Hashtable的第index位置,对于写入,这和数组一样,把一个对象

放在其中的第index位置,但如果是查询,经过同样的算法,Hashtable可以直接从第index

取得这个对象,而数组却要做循环比较.所以对于数据量稍大时,Hashtable的查询比数据

具有更高的性能.

既然可以根据HashCode直接定位对象在Hashtable中的位置,那么为什么Hashtable

要用key来做映射呢(为了一些思维有障碍的人能看到懂我加了一句话:

而不是直接放value呢)?

这就是关系Hashtable性能问题的最重要的问题:

Hash冲突.

常见的Hash冲突是不同对象最终产生了相同的索引,而一种非常甚至绝对少见的Hash冲突

是,如果一组对象的个数大过了int范围,而HashCode的长度只能在int范围中,所以肯定要

有同一组的元素有相同的HashCode,这样无论如何他们都会有相同的索引.当然这种极端

的情况是极少见的,可以暂不考虑,但对于相同的HashCode经过取模,则会产中相同的索引,

或者不同的对象却具有相同的HashCode,当然具有相同的索引.

所以对于索引相同的对象,在该index位置存放了

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1