一. 关于==

==是容易理解的。java设计java就是要比较两个对象是不是同一个对象。

对于引用变量而言,比较的时候两个引用变量引用的是不是同一个对象, 即比较的是两个引用中存储的对象地址是不是一样的。 并且和该对象的toString方法无关,即使重写类的toString方法,返回不同的值也不会影响该结果。

对于基本数据类型而言,比较的就是两个数据是不是相等。

二. 关于equals()

有时候当两个对象= =为false时,我们仍然会认为两者是“相等”的,比如对于String对象,当两个对象的字符串序列是一致的,我们就认为他们是“相等”的。对于这样的需求,需要equals()来实现。对于有这种需求的对象的类,重写其equals()方法便可。

Object中equals()的默认实现是比较两个对象是不是==,即其和==的效果是相同的。

三. 关于hashCode()

Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

目前常用的hash算法如MD5,SHA-1等。

hashCode()方法返回的就是一个数值,我们称之为hashCode吧。从方法的名称上就可以看出,其目的是生成一个hash码。hash码的主要用途就是在对对象进行散列的时候作为key输入,据此很容易推断出,我们需要每个对象的hash码尽可能不同,这样才能保证散列的存取性能。事实上,Object类提供的默认实现确实保证每个对象的hash码不同(在对象的内存地址基础上经过特定算法返回一个hash码)。很多类都会重写该hashCode方法,比如String的hashCode方法会根据String的值的内容计算hashCode值,所以String等重写hashCode的类的hashCode和引用地址无关,而是和具体内容有关。

Object的toString方法return getClass().getName() + "@" + Integer.toHexString(hashCode());
● 同一实例的hashcode值不会随着对象属性的变化而变化
● 每次启动程序后,相同位置(第几个创建)创建的对象实例(不论反射还是new),hashCode值相同(???猜测与class的存储地址+当前创建的次数有关,因为即使调用System.gc()也不会改变下次实例化的hashCode值)

对于集合类HashSet、HashMap等和hash有关的类(以HashSet为例),是通过hash算法来散列对象的。对HashSet而言,存入对象的流程为:根据对象的hash码,经过hash算法,找到对象应该存放的位置,如果该位置为空,则将对象存入该位置;如果该位置不为空,则使用equals()比较该位置的对象和将要入的对象,如果两个相等,则不再插入,如果不相等,根据hash冲突解决算法将对象插入其他位置。
而java规定对于HashSet判断是不是重复对象就是通过equals() 方法来完成,这就需要在两个对象equals()方法相等的时候,hash码一定相等(即hashCode()返回的值相等)。假设两个对象equals()方法相等的时候,hash码不相等,会出现equals()相等的两个对象都插入了HashSet中。

set虽然是无序的,但是该无序指的是set不会按插入的顺序保存数据,而是会根据hashcode的顺序保存数据。

分析以下情况
○ 重写hashcode返回固定的数字,是否可存储同一class的不同实例。是
○ 重写equal返回固定值true,是否可存储同一class的不同实例。是,但不应该。
○ 重写hashCode返回固定数字,重写equals方法返回true,是否可存储同一class的不同实例。否
○ 重写hashCode返回不同数字,同一class的同一实例是否可存储多次。是

四. 总结

对于equals()相等的两个对象,其hashCode()返回的值一定相等
结合前面关于hash码要尽可能不同的要求,现在变成了对于equals()相等的对象hash码要一定相等,而对于equals()不同的对象要尽量做到hash码不同。那么怎么才能保证这一点呢?重写hashCode()
首先,如何保证“对于equals()相等的对象hash码要一定相等”。
equals()方法中对于对象的比较是通过比较对象中全部或者部分字段来完成的,这些字段集合记为集合A,如果我们计算hash码的时候,如果只是从集合A中选取部分字段或者全部字段来完成便可,因为输入相同,不管经过什么算法,输出一定相同(在方法中调用随机函数?这属于吃饱了撑的!)。如此设计便保证满足了第一个要求。
其次,对于equals()不同的对象要尽量做到hash码不同。
对于这一点的保证就是在设计一个好的算法,让不同的输入尽可能产生不同的输出。