java中hashcode()和equals()

这阵子工作有点小空,工作忙完了,顺便巩固下基础但很少有人去在意的知识。

1、equals()和hashcode()方法都是从object类中继承过来,其中equals方法在object类中的定义如下:

public boolean equals(Object obj) {
	return (this == obj);
 }


大家知道,这个是对两个对象在内存中的地址的比较(两个引用对象是否相等)。Math、String、Integer、Double等等封装类在使用equals方法的时候,都重写了object类的equals方法,重写的都是对内容(值)进行比较,不再是对地址进行比较。各自的代码实现,大家可以去看源码。这里列出一个Integer重写equals的源码:

    public boolean equals(Object obj) {
	if (obj instanceof Integer) {
	    return value == ((Integer)obj).intValue();
	}
	return false;
    }


从外部调用Integer的equals方法的时候,会先判断参数是否为Integer类型,如果不是直接返回false。如果是,就比较实际的存储的值。例如:

		Integer integer = new Integer(2);
		Integer integer2 = new Integer(2);
		System.out.println(integer.equals(integer2));//返回true

摘自《effective java》:

在覆盖equals方法的时候,必须遵守它的通用规定。一下是约定的内容,来自Object的规范:

equals实现了等价关系(equivalence relation):

自反性,对于任何非null的引用值x,x.equals(x)必须返回true。

对称性,对于任何非null的引用值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。

一致性,对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true或者一致的返回false。

对于任何非null的引用值x,x.equals(null)必须返回false。

以上这五点是重写equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守。


2、hashcode方法,在Object类中定义如下:

    public native int hashCode();

说明是一个本地方法,它的实现是跟本地机器相关的,当然也可以自己在类里面重写hashcode方法。

摘自Object规范(JavaSE6):

如果俩对象equals相等,那么hashcode值一定相等;

如果equals不想等,hashcode有可能相等,也有可能不想等;

如果hashcode不想等,那么equals一定不想等;

如果hashcode相等,equals可能相等,也有可能不想等。


记得以前刚毕业的时候,看到过一个面试题,怎样判断Set中的元素是否重复,那么HashSet是根据什么原理来存取对象的?因为HashSet中不允许有重复的对象,那么,怎样判断是否重复了?

看代码:

@Test
	public void testHashEqual() throws Exception {
		String s1 = new String("文三路");
		String s2 = new String("文三路");
		System.out.println(s1 == s2);//false
		System.out.println(s1.equals(s2));//true
		System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode()
		System.out.println(s2.hashCode());
		Set hashset = new HashSet();
		hashset.add(s1);
		hashset.add(s2);
		Iterator it = hashset.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
	}


刚才说过,equals相等,hashcode一定相等,所以,这里HashSet会认为s1和s2是相等的,属于重复添加,所以s2会覆盖s1.打印结果:

false
true
25632973
25632973
文三路


再看领一个例子:

@Test
	public void testRewriteHashEqual() {
		HashSet hs = new HashSet();
		hs.add(new UserInfo(1, "文三路"));
		hs.add(new UserInfo(2, "文二路"));
		hs.add(new UserInfo(3, "文一路"));
		hs.add(new UserInfo(1, "文三路"));

		Iterator<UserInfo> it = hs.iterator();
		while (it.hasNext()) {
			UserInfo user = it.next();
			System.out.println(user.getAge()+",,"+user.getName());
		}
	}



打印结果:

3,,文一路
1,,文三路
2,,文二路
1,,文三路

我们可以看到,有两个对象的age和name分别都是:1、文三路,添加了相等的元素,为什么没有覆盖?  是不是违反了HashSet的原则?没有的。因为:

在new UserInfo(1,"文三路“);的时候,生成的是不同的hashcode,且此时equals也不相等,equals比较的是引用对象的地址,new两次,两次的内存地址肯定不会相同,所以new出来的肯定不会equals。

刚才的s1和s2不是生成了相同的hashcode吗?因为String重写了hashcode这个方法,同样,我们也可以重写UserInfo的equals和hashcode方法。


在UserInfo里面加入以下代码:

@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;

		UserInfo userInfo = (UserInfo) o;

		if (age != userInfo.age) return false;
		if (name != null ? !name.equals(userInfo.name) : userInfo.name != null) return false;

		return true;
	}

	@Override
	public int hashCode() {
		int result = age;
		result = 31 * result + (name != null ? name.hashCode() : 0);
		return result;
	}

这里重写了equals和hashcode方法,重写的时候是把每一属性的值加都作为计算的一部分,所以如果属性值都一样,就会true。

再次运行:

2,,文二路
3,,文一路
1,,文三路


为什么要用hashcode?

当HashSet中元素比较多的时候,或者重写equals比较复杂的时候,如果只用equals进行判断,效率会非常低,要匹配HashSet里面的每一个元素的值。

所以,引入hashcode方法,可以提升效率,如果不引入hashcode,判断元素是否重复,效率大大降低。java语言规范允许通过获取hashcode的值来判断是否重复。


网上百度、谷歌了好多资料,加上自己的理解和整理,记录了下来。便于后人乘凉。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值