作业帮 > 体裁作文 > 教育资讯

hashcode改写

来源:学生作业帮助网 编辑:作业帮 时间:2024/11/02 03:39:40 体裁作文
hashcode改写体裁作文

篇一:改写hashcode

Item 8改写equals方法时总是要改写hashCode方法- -

一 改写的原因

Java.lang.Object规范中对hashCode作了如下规定

1 同一对象多次调用时,返回同一个hashCode值,这个值可以改变。

2 相等对象,hashCode值必须相等

3 不等对象,hashCode值是否不等没有硬性规定

如果只改写equal方法而没有改写hashCode方法,将违反第二条规范。

public class PhoneNumber{

public boolean equals(Object o) {

if(o == this){

return true;

}

if(!(o instanceof PhoneNumber)){

return false;

}

PhoneNumber pn = (PhoneNumber)o;

return pn.extension == extension

&& pn.exchange == exchange

&& pn.areaCode == areaCode;

}

如果用如下的方式存放:

Map m = new HashMap();

m.put(new PhoneNumber(1,2,3),"Jenny");

用如下的方式取出

m.get(new PhoneNumber(1,2,3));

预期的结果是:

Jenny

实际结果是:

null

因为put和get时的key是new出来的2个不同的对象,在内存中的存放地址不一致,如果没有改写hashCode方法,则它们的hashCode值是不一致的,而改写过后的equals方法认为它们是一致的,这就

违反了规范的第二条。因此需要改写hashCode方法。

二 改写的方法

hashCode取值的一些要求

定义一个初始值 result = 17

对每个域计算hashCode

boolean -- f ? 0 : 1

byte,char,short,int -- (int)f

long -- (int) (f ^ (f >>> 32))

float -- Float.floatToIntBits(f)

double -- Double.dubleToLongBits(f)

Object -- 对euqal方法涉及到的每个member作hashCode,如果为null,返回0

Array -- 每个element单独处理

每步计算出来的值result代入如下公式,计算总和

result = 37 * result + c

计算出来的hashCode=result,其实是hashTable,hashMap,hashSet这些collection的下标值,因此需要取模,以免越界。同时初始值的选择尽量选择非0的质数。原因只在于避免hashCode过分集中。

三 注意事项

在这个过程中需要注意的有如下几点:

1 如果计算过程过于复杂,可以只进行一次计算,将结果缓存在对象内部中,由对象自己对这个值进行管理和维护

2 计算hashCode时不应该忽略调关键域,以免不同对象的hashCode值过分集中

3 equal方法反应出来的是两个对象逻辑上是相等的,而hashCode相等表示这2个对象可能逻辑上是相等的。

4 hashCode先于equal方法,在hash collection中,查找,插入,删除对象都是先计算这个对象A的hashCode值,然后再去找hashCode对应位置的对象B,进行equal的比较。

5 hashCode的引入是为了加快速度。

6 在其他collection中,hashCode没有多大用处。

补注:对hashCode函数来说,返回为int类型,而java中中内置运算符没有overflow和underflow情况的,因此hashCode计算出来的值不会发生异常,对计算出来的结果值再次进行hash,从而影射到hashTable中对应的索引位

篇二:Hashcode()用法

Hashcode()用法

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

0 1 2 3 4 5 6 7

而我有个类,这个类有个字段叫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(List list)对其进行排序

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

然后通过Collections.sort(List list,B b)进行排序

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

public class Test {

public static void main(String[] args) {

HashSet set = new HashSet();

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

set.add(new Demo1(i,i));

}

System.out.println(set);

set.add(new Demo1(1,1));

System.out.println(set);

System.out.println(set.contains(new Demo1(0,0)));

System.out.println(set.add(new Demo1(1,1)));

System.out.println(set.add(new Demo1(4,4)));

System.out.println(set);

}

private static class Demo1 {

private int value;

private int id;

public Demo1(int value,int id) {

this.value = value;

this.id=id;

}

public String toString() {

return " value = " + value;

}

public boolean equals(Object o) {

Demo1 a = (Demo1) o;

return (a.value == value) ? true : false;

}

public int hashCode() {

return id;

}

}

}

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

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

public final class Test {

public static void main(String[] args) {

Map m = new HashMap();

m.put(new PhoneNumber(020, 12345678), "shellfeng");

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

}

private static class PhoneNumber {

/**

* 区号

*/

private short areaCode;

/**

* 扩展号

*/

private short extension;

public PhoneNumber(int areaCode, int extension) {

this.areaCode = (short) areaCode;

this.extension = (short) extension;

}

public boolean equals(Object o) {

if (o == this) {

return true;

}

if (!(o instanceof PhoneNumber)) {

return false;

}

PhoneNumber pn = (PhoneNumber) o;

return pn.extension == extension && pn.areaCode == areaCode;

}

/**

* @see java.lang.Object#hashCode()

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

*/

public int hashCode() {

int result = 17;

result = 37 * result + areaCode;

result = 37 * result + extension;

return result;

}

}

}

还是那句话:你注释掉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改写

新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直 接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列 其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

改写equals时总是要改写hashCode

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

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

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

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

3. 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。

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

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

Object的代码:

public String toString () {

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

}

public boolean equals (Object o) {

return this == o;

}

/**

* Answers an integer hash code for the receiver. Any two

* objects which answer true when passed to

* .equals must answer the same value for this

* method.

*

* @author OTI

* @version initial

*

* @return int

* the receiver's hash.

*

* @see#equals

*/

public native int hashCode();

篇三:覆盖equals()与hashCode()

一切理论都是空话,只有结合具体实例来证明你的理论,理论才称得上真正的理论!我们在学习Java的过程中,要掌握的知识太多,只有我们自己去把书上或网上的理论结合上自己的实例,那我们才算是真正的掌握。要不然,我们只会是暂时性的理解!

Java对于equals方法和hashCode方法是这样给定的:

1)如果两个对象相同,那么他们的hashCode值一定相同;

2)如果连个对象的hashCode相同,它们并不一定相同

HashCode 是一种编码方式,在Java中,每个对象都会有一个hashCode,Java可以通过这个hashCode值来识别一个对象。HashCode实现了一种无顺序元素排列

Java语言对equals()的要求如下,这些要求是必须遵循的:

1)自反性(反射性):对于任何非空的引用x,x.equals(x)必须返回true。

2)对称性:对于任何非空的引用x,如果x.equals(y)返回时true,那么y.equals(x)

也应该返回时true。

3)传递性(类推性):如果x.equals(y)返回时true,那么y.equals(z)也应该返回时

true,那么z.equals(x)也应该返回true。

4)一致性:如果x.equals(y)返回时true,只要x和y内容一直不变,不管你重复

x.equals(y)多少次,返回都是true。

5)任何情况下,x.equals(null),永远返回是false;x.equals(和x不同类型的对象

永远返回false)。

equals()方法和hashCode()这两个方法都是从object类中继承过来的。

equals()方法在object类中定义如下:

public boolean equals(Object obj) {

return (this == obj);

}

可以看出equals()方法比较引用是否相同。但在String、Math、Integer、Double…等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。

如在String类中equals()方法定义如下:

public boolean equals(Object anObject) {

if (this == anObject) {

return true;

}

if (anObject instanceof String) {

String anotherString = (String) anObject; int n = count;

if (n == anotherString.count) {

char v1[] = value;

char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++])return false; } return true;} } return false; }

很明显,这是进行的内容比较,而已经不再是地址的比较。一次类推Double、Integer、Math…等等这些类都是重写了equals()方法的,从而进行的是内容的比较。基本类型是进行值的比较,不在介绍!

hashCode()方法,在object类中定义如下:

public native int hashCode(); 说明这是一个本地方法,它的实现是根据本地机器相关的。同样我们可以在自己写的类中覆盖hashCode()方法,比如String、Math、Integer、Double…等等这些类都是覆盖了hashCode()方法方法的。

例如在string类中定义的hashCode()方法如下:

public int hashCode() {

int h = hash;

int len = count;

if (h == 0 && len > 0) {

int off = offset;

char val[] = value;

for (int i = 0; i < len; i++) {

h = 31*h + val[off++];

}

hash = h;

}

return h;

}

这里我们首先要明白一个问题:

equals()相等的两个对象,hashCode()一定相等;

equals()不相等的两个对象,却不能证明他们的hashCode()不相等。换句话说,equals()不相等的两个对象,hashCode()有可能相等。(我的理解是由于哈希码在生产的时候产生冲突造成的)。

反过来:hashCode()不相等一定能推出equals()也不相等;hashCode()相等,equals()可能相等也可能不相等。

程序实例:

public class EqualsDemo {

public static void main(String[] args) {

System.out.println("==是比较两个变量的值是否相等!");

Person p1=new Person(1);

Person p2=p1;

System.out.println(p1.hashCode());//12677476System.out.println(p2.hashCode());//12677476System.out.println(p1);//wh.Person@c17164System.out.println(p2);//wh.Person@c17164

System.out.println("p1==p2:"+(p1==p2));//p1==p2:true

Person p3=new Person(1);

System.out.println(p3.hashCode());//33263331System.out.println(p3);//wh.Person@1fb8ee3

System.out.println("p1==p3:"+(p1==p3));//p1==p2:false

//p1和p3引用的对象在逻辑上是同一个

System.out.println("Object.equals()方法用于比较对象是否相等!");

//默认的equals()方法是比较对象的地址!建议覆盖修改!System.out.println("p1.equals(p2):"+p1.equals(p2));//true

System.out.println("p1.equals(p3):"+p1.equals(p3));//false

}

}

class Person{

int id;

String name;

public Person(int id){

this.id=id;

}

}

由上面的实例我们可以看出默认的equals()方法是比较对象的地址!这种比较方法很有局限性,比如:我们判断是否是同一个人是根据一个人的身份证号码(身份证号码可以唯一标识一个人),但是

如果按默认的比较方式,那么两个对象他们的地址不同,就会返回false,但是他们的身份证号码相同,却是同一个人。

再比如:两幅扑克牌,如果花色和点数相等我们就认为这两张牌相等,但这却是两个不同的对象。若是一副牌,我们只需判断点数相等即可认为这两张牌是相等的,而无需判断花色!因此,在现实生活中重写equals()方法是很有必要的,而且SUN公司也建议我们把equals()、hashCode()、toString()等Object类里面的方法重写。可以自己查看API帮助文档!

下面的程序是对equals()、hashCode()改写后的程序!

public class EqualsDemo2 {

public static void main(String[] args) {

//在EqualsDemo类中我们举的例子中p1和p3引用的对象在逻辑上是同一个

System.out.println("Object.equals()方法用于比较对象是否相等!");

//默认的equals()方法是比较对象的地址!建议覆盖修改!//覆盖为根据对象的“关键属性”判断对象是否相等!

Person2 p1=new Person2(1);

Person2 p2=p1;

Person2 p3=new Person2(1);

System.out.println("p1.equals(p2):"+p1.equals(p2));//true

System.out.println("p1.equals(p3):"+p1.equals(p2));//true

}

}

class Person2{

int id;

String name;

public Person2(int id){

this.id=id;

}

/*

* 重写equals()方法,人的身份证不能重复,我们判断是否是同

* 一个人的根据是他们的id(身份证)是否相等!

*/

public boolean equals(Object obj){

//我们必须遵循equals()方法的覆盖(重写)规则

//规则一:任何情况下,x.equals(null),永远返回falseif(obj==null)

return false;

//规则二:自反性,x.equals(x)必须返回true。

if(this==obj)

return true;

//

if(obj instanceof Person){//相同类型的比较 Person2 other=(Person2)obj;

return this.id==other.id;

}

return false;

}

public int hashCode(){

return id;

}

}

体裁作文