hashCode和equals详解

最近在重温java源码,一边看一边总结下,并且分享下自己的心得,共同学习,欢迎指点。这一篇说下hashCode和equals的源码中的一些注意点,争取把原理讲透彻:

源码
平时用hashMap多会用到get,put,iterator等方法,在这些方法里面都能发现hashCode()和equals的身影,它是生成hashMap的key的重要步骤,所以在这里做下深入并延伸下。
public native int hashCode();
这是Object中的方法,可以发现是个本地方法。但是许多类都会覆盖这个方法比如String,Integer,Long等基础类。所以不同的类有不同的散列方式,如Integer类只是单纯的返回了value;
public int hashCode() {
	return value;
 }

而String则会遍历每个字符,并且做【h = 31*h + val[off++]】这样一个运算,h=0,value为char数组。
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;
    }


而默认的本地方法实现方式是./hotspot/src/share/vm/runtime/synchronizer.cpp +530  get_next_hash方法
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0 ;
  if (hashCode == 0) {
     value = os::random() ;
  } else
  if (hashCode == 1) {
     intptr_t addrBits = intptr_t(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     value = intptr_t(obj) ;
  } else {
     unsigned t = Self->_hashStateX ;
     t ^= (t << 11) ;
     Self->_hashStateX = Self->_hashStateY ;
     Self->_hashStateY = Self->_hashStateZ ;
     Self->_hashStateZ = Self->_hashStateW ;
     unsigned v = Self->_hashStateW ;
     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
     Self->_hashStateW = v ;
     value = v ;
  }
  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash, "invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}
根据hashCode的不同情况做不同处理,如1的时候返回随机数,2则返回跟地址有关的一个数值。具体逻辑含义需要仔细看下jvm源码。

public boolean equals(Object obj) {
	return (this == obj);
}
这是equals的源码,没有特殊,判断地址。


注意项:
1:一个好的hash算法和一个差的hash算法相差很多,好的hash算法可以使HashMap的get无限趋于O(1),而差的hash算法可以无限趋于O(n)。
好的hash算法为不同的对象产生不同的hashCode。
2:覆盖equals时候的约定:
自反性:对于任何非null的引用值x,x.equals(x)必须返回true。
对称行:对于非空x,y  当且仅当y.equals(x)返回true时,x.equals(y)必须返回true
传递性:对于任何非null的引用x,y,z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true
一致性:如果对于非空x,y,并且没有修改,多次调用x.equals(y)必须相等
对于任何非null的x,x.equals(null)必须返回false
3:当覆盖equals时总要覆盖hashCode
考虑如下代码
public class MyInt {
	public int value ;
	
	MyInt(int value){
		this.value=value; 
	}
	public boolean equals(Object o){
		if(this == o)
			return true;
		if(!(o instanceof MyInt))
			return false;
		MyInt mi = (MyInt)o;
		return (this.value == mi.value);
	}
}

假设此时你想用这个类作为key与hashmap一起使用:
Map<MyInt,String> map = new HashMap<MyInt,String>();
map.put(new MyInt(2), "test");
System.out.println(map.get(new MyInt(2)));
如果你期望返回test那么就错了。你可能以为重写了equals方法,用hashmap就会对应到一个key上。不会的 。hashmap用的是hashcode进行散列的。两个不一样的对象在hashCode看来是不一样的。如果hashMap的hashCode恰巧也相等了,好吧恭喜你,中奖了。
e.hash == hash && ((k = e.key) == key || key.equals(k))

hashMap中的判断条件是这样的,因为MyInt重写了equals,又恰巧hashCode也相同,所以可以返回。但是千万不要抱这种侥幸心里。
所以通过以上分析,在重写equals方法后一定要重写HashCode
4:总结性的,没有特别必要的情况不要重写equals方法;如果一定要重写equals方法,那么一定要重写hashCode方法,并且要找一个效率高的hash算法。如果你不是一个大牛可以考虑看下《effective java》,里面介绍了一种比较通用的效率也很高的算法。如果是大牛,求分享算法。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中,hashCode()是Object类的一个方法,返回对象的哈希码。哈希码是由对象的存储地址或者其他信息计算得到的一个int类型的整数。 在Java中,哈希码经常被用来实现数据结构中的散列表。在散列表中,每个对象都有一个对应的哈希码,通过哈希码可以快速定位到对象存储的位置,从而实现快速的查找、插入和删除等操作。 Java中的hashCode()方法的默认实现是返回对象的存储地址的一个int类型的整数表示,即对象的内存地址。但是,不同的JVM实现可能会有不同的实现方式。 在实际开发中,我们可以根据对象的属性计算哈希码,以实现更好的散列表性能。具体来说,我们需要注意以下几点: 1. hashCode()方法的返回值应该尽可能地分散,避免不同的对象产生相同的哈希码。这可以通过使用对象的不同属性进行计算来实现。 2. 如果两个对象的equals()方法返回true,那么它们的hashCode()方法应该返回相同的值。这可以确保这两个对象在散列表中的位置是相同的。 3. hashCode()方法的返回值应该在对象的生命周期中保持不变。如果对象的属性发生变化,那么它的哈希码也应该发生变化。 总之,hashCode()方法在Java中是非常重要的一个方法,我们可以根据对象的属性计算哈希码,以实现更好的散列表性能。同时,我们需要注意hashCode()方法的实现要尽可能地分散,避免不同的对象产生相同的哈希码,同时也要保证hashCode()方法的返回值在对象的生命周期中保持不变。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值