码迷,mamicode.com
首页 > 编程语言 > 详细

Java功底篇系列-01-从==/equals/hashCode开始

时间:2015-08-09 19:05:41      阅读:256      评论:0      收藏:0      [点我收藏+]

标签:java equals == hashcode

前言

自己毕业后做JAVA开发已经2年了,从最初的JAVA WEB开发,到投入到Hadoop大数据开发潮流中,越来越发现自己的JAVA基础,数据结构,多线程等等,是那么的脆弱!还有什么比基础更加重要呢,应该静下心来,去学习它们,就从这一篇博客开始吧~


话题一:==与equals


让我们首先看看Object类中定义的equals方法的源码:


技术分享


在Object类中,equals就是利用==实现对象之间的比较的。


我们知道对于基本数据类型,==比较的就是他们的值是否相等,那么对于对象类型的==比较的是什么呢?


对于对象类型,a==b比较的就是a和b是否就是同一个对象!


很显然,我们常常需要做的就是比较2个对象是不是从业务的角度相等,而并不是想比较他们是不是同一个对象。因此我们需要override equals()方法。


比如String就是有这样的需求,因此我们来看看她是怎么复写equals()的:


技术分享


String的obj1.equals(obj2)实现的步骤:


第一,利用==看一下,obj1和obj2是不是同一个对象,如果是那么则肯定equals的!


第二,他们的类型相同吗?长度相同吗?


第三,开始逐个字符进行==比较,如果出现不同,则可以得到结果,否则继续比较。


感悟:


以后,我们去实现自定义类的equals方法时,是不是要考虑下:


他们是同一个对象吗?


他们是同一个数据类型吗?


他们自身有哪些关键属性,是否通过比较这些关键属性来快速得到结论呢?




话题二:equals与hashCode


hashCode(),和equals()一样,都是在Object中定义的,我们来看看吧:


技术分享


很不幸,在Object中他是一个native调用,我们还是看看String中的hashCode是如何是实现的:


技术分享


分析:


第一,可以看出,一旦一个String在调用hashCode方法后,那么hash属性就会得到一次赋值,那么以后再次调用这个hashCode方法,就会直接返回hash属性,而不再计算了。


第二,从hashCode方法内部的for循环可以看出,这个hash值,是一个综合值,是综合了每一个字符计算得到的。


想法:


如果2个String他们的字符内容完全一样,那么显然他们的hashCode理应一样!


如果2个String他们的字符内容不同,那么hashCode就不同吗?根据上面的分析,hashCode是一个综合值,字符内容不同,完全可以得到一样的hashCode!


那如果hashCode一样,那也不到任何结论!


那如果hashCode不一样,那么对于String而言,他们的字符内容肯定不同!



关系:


String的字符内容的比较正是equals方法,那么equals与hashCode是什么关系呢?


以前,我以为自定义一个类,就应该重写这2个方法,他们应该是“天生一对”。其实不是!


上面的话题,其实已经说明,我们重写equals方法是为了从业务角度来实现2个对象的比较,那hashCode是干嘛的呢?


我们知道算法的基础是数字,比如hash算法。在map中,很显然如果KEY是一个对象的话,那么这个对象应该实现HASH的查找和写入,可是他是一个对象啊,他应该提供一个数字,以便于实现HASH算法!


也就是说,hashCode是为了给对象一个数字,这个数字用于HASH这种数据结构中,去实现快速定位对象用的!hashCode定位链表后,其实还是得循环链表利用equals进行比较。


那equals和hashCode其实没有什么关系,不是非要在一起的,一个是为了比较对象是不是业务上相等,一个是为了HASH快速定位而存在的。



话题三:String的==迷惑


简单看一段代码吧:


技术分享


结果是:


true

false


对于String而言,equals我们犯不了什么错误,可是==是经常让我们迷惑的地方!


第一,String在编译期存在优化行为


例如,String s = "a" + "b"; 在编译生成的CLASS文件中已经是这样的了:


String s = "ab";


那么上面的代码中,s4会在编译期就变成"abc"吗?不会,因为s2是一个变量,只能在运行期间确定,而且并没有强制约束他不能变。而s5在编译期间就已经确定为"abc",因为s3是final的,变不了的!


是这样吗?


我们来利用反汇编命令javap命令看下:


技术分享


很多指令,我也不懂,我们只看能看得懂的地方,或者猜一猜~


从0到8,根据注释String ab,应该是对应于代码的


技术分享


从而也验证了,String存在编译期合并的事实。



从9到28,发生了new 操作,还有append,这应该对应于:


技术分享


这说明,对于String而言,变量与常量的这种“+”操作,实际上在底层是搞成了一个StringBuilder进行append,最后在来一个toString得到的!


从30到32,根据注释String abc,应该对应于代码:


技术分享


这也说明了,final变量与常量的这种“+”操作,直接编译合并!



第二,String存在常量池的概念


在编译期间就确定的String常量,是存在String pool中的,而且是保证全局唯一的。也就是说上面代码中,s1/s2指向String Pool中的同一个string。


相信到这里以后String的==以后不会在迷糊了,呵呵~



话题四:String +  VS  StringBuffer.append  谁更快?


我们都说,StringBuffer.append好,她更快?


根据话题三的分析,String的“+”可能在编译期间就合并了,而StringBuffer.append是必须在运行期间进行的操作,很显然,从这种角度而言,应该String的“+”更快些!


那么以前是怎么回事呢?


技术分享


对于第一个for循环而言,注意到

s += i;

根据上面的分析,这是要发生new StringBuilder,append,toString操作的。

也就是,我们在一个大大的for循环中进行了new操作!真是不可思议!这并不是我们预料的!

我们暂且不考虑StringBuilder扩容机制,要知道随着循环的进行,字符串内容越来越大,new产生的垃圾对象也越来越大,可能内存不足了,GC要开始忙碌了!这应该就是程序性能下降的原因。


而对于第二个for循环而言,

没有new操作,虽然涉及到扩容,但是没有那么多的垃圾。



这篇博客就到这里了,收获很多,希望对大家也有帮助~


本文出自 “努力奋斗” 博客,请务必保留此出处http://zhangfengzhe.blog.51cto.com/8855103/1683059

Java功底篇系列-01-从==/equals/hashCode开始

标签:java equals == hashcode

原文地址:http://zhangfengzhe.blog.51cto.com/8855103/1683059

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!