Java基础-重写equals()为什么要重写hashCode(),什么时候重写?

我们在使用HashSet集合存放对象时,判断对象是否相等的逻辑往往是自己实现,不会直接使用默认的,为什么呢?举个例子

package equals;

import java.util.HashSet;

/**
 * 为什么重写Equals一定要重写HashCode呢?
 * 
 * @author Administrator
 *
 */
public class HashCodeAndEquals {
	
	public static void main(String[] args) {
		
		HashSet<TT> tts = new HashSet<>();
		TT t1 = new TT(1);
		TT t2 = new TT(1);
		TT t3 = new TT(2);
		
		tts.add(t1);
		tts.add(t2);
		tts.add(t3);
		
		System.out.println(tts.size());
		System.out.println(t1.equals(t2));
		System.out.println(t1.hashCode());
		System.out.println(t2.hashCode());
	}
	
}


class TT{
	
	private int v;
	
	public TT() {
		
	}
	public TT(int v) {
		this.v = v;
	}
	
}

输出:

3
false
2018699554
1311053135

 

在大多数情况下,我们定义的t1,t2会在逻辑上认为是同一个对象,但是HashSet认为,这两个是不同的对象(在虚拟机中t1与t2确实是两个不同的对象),此时我们在重写equals让t1与t2是同一个对象。

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof TT) {
			TT t = (TT)obj;
			return t.v == this.v;
		}
		return false;
	}

添加如上代码,运行输出:

3
true
2018699554
1311053135

 

 

t1.euqals(t2)确实返回true但是,集合中任然是三个不同的对象,在重写hashCode()

	@Override
	public int hashCode() {
		int result = 17; 
        result = 31 * result + String.valueOf(v).hashCode();
        return result;
	}

运行输出:

2
true
576
576
 

由此,才能真正满足我们实际的需求。

 

下面是转载:https://www.cnblogs.com/yangqiong1989/p/10795049.html

HashMap的put和get也类似。
HashMap是底层实现时数组加链表。
       A.当put元素时:
              1.首先根据put元素的key获取hashcode,然后根据hashcode算出数组的下标位置,如果下标位置没有元素,直接放入元素即可。
              2.如果该下标位置有元素(即根据put元素的key算出的hashcode一样即重复了),则需要已有元素和put元素的key对象比较equals方法,如果equals不一样,则说明可以放入进map中。这里由于hashcode一样,所以得出的数组下标位置相同。所以会在该数组位置创建一个链表,后put进入的元素到放链表头,原来的元素向后移动。       
        B.当get元素时:
             根据元素的key获取hashcode,然后根据hashcode获取数组下标位置,如果只有一个元素则直接取出。如果该位置一个链表,则需要调用equals方法遍历链表中的所有元素与当前的元素比较,得到真正想要的对象。
可以看出如果根据hashcdoe算出的数组位置尽量的均匀分布,则可以避免遍历链表的情况,以提高性能。
所以要求重写hashmap时,也要重写equals方法。以保证他们是相同的比较逻辑。

 

来看一下Object.hashCode的通用约定(摘自《Effective Java》第45页)

在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,那么,对该对象调用hashCode方法多次,它必须始终如一地返回 同一个整数。在同一个应用程序的多次执行过程中,这个整数可以不同,即这个应用程序这次执行返回的整数与下一次执行返回的整数可以不一致。
如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果。
如果两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任一个对象的hashCode方法,不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生截然不同的整数结果,有可能提高散列表(hash table)的性能。
     如果只重写了equals方法而没有重写hashCode方法的话,则会违反约定的第二条:相等的对象必须具有相等的散列码(hashCode)。

     同时对于HashSet和HashMap这些基于散列值(hash)实现的类。HashMap的底层处理机制是以数组的方法保存放入的数据的(Node<K,V>[] table),其中的关键是数组下标的处理。数组的下标是根据传入的元素hashCode方法的返回值再和特定的值异或决定的。如果该数组位置上已经有放入的值了,且传入的键值相等则不处理,若不相等则覆盖原来的值,如果数组位置没有条目,则插入,并加入到相应的链表中。检查键是否存在也是根据hashCode值来确定的。所以如果不重写hashCode的话,可能导致HashSet、HashMap不能正常的运作、

    如果我们将某个自定义对象存到HashMap或者HashSet及其类似实现类中的时候,如果该对象的属性参与了hashCode的计算,那么就不能修改该对象参数hashCode计算的属性了。有可能会移除不了元素,导致内存泄漏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值