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再求余,余数几是计算得到的散列值,我们就认为返回一个不是物理地址的数值,而是一个可以映射到物理地址的值)。
这 样一来,当集合要添加
改写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;
}
}
体裁作文