在Object中提供了equals()和hashCode()方法,每一个类都可以重写这两个方法,一般equals()用于判断两个类是否相同,而hashCode()方法用于计算对象的哈希值
hashCode方法
作用
一般在需要使用哈希表的类(Hashtable,HashMap..)中使用,用于寻域
Hash是散列的意思,就是把任意长度的输入,通过散列算法变换成固定长度的输出,该输出就是散列值。关于散列值,有以下几个关键结论:
1、如果散列表中存在和散列原始输入K相等的记录,那么K必定在f(K)的存储位置上
2、不同关键字经过散列算法变换后可能得到同一个散列地址,这种现象称为碰撞
3、如果两个Hash值不同(前提是同一Hash算法),那么这两个Hash值对应的原始输入必定不同
当要将数据存放到集合中可以根据hashCode快速映射到指定位置,使得查找的时候无需挨个查找,查找的时间复杂度可以是常量级
例如在HashMap中存放元素,首先使用hashCode和底层数组长度进行取模运算
ps:这里和0x7FFFFFFF进行与运算是为了防止出现负值
return (hash & 0x7FFFFFFF) % table.length
得到映射到数组中的下标位置,如果当前空间未存放元素,那么直接插入即可;否则证明产生了哈希冲突,HashMap解决哈希冲突使用的是拉链法,于是使用equals()方法一个个的比较,如果相等证明数组中已经存放了该键值,那么直接覆盖value即可;否则插入到表尾。
设计规则
- 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。
- 如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。
- 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。
简而言之就是
- 如果x.equals(y)返回“true”,那么x和y的hashCode()必须相等。
- 如果x.equals(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不等。
注意事项
- hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
- 如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
- 如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
- 两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
底层实现
对简单的基本数据类型,像double, int, char等类型, 由于它们不是Object, 所以它们没有hashCode()的方法
而各种基本数据类型对应的包装类,底层实现如下
- Boolean
- return 1231 or 1237;
- Byte
- return the byte value ranging from -128 to 127
- 直接返回对应的基本数据类型的值
- Character
- return the character ASCII Code
- 也是直接返回字符对应的int值
- Short
- return the short value
- 直接返回对应的基本数据类型的值
- Integer
- return the int value
- 返回对应的整型值
- Long
- return (int)(value ^ (value >>> 32));
- 返回高32位和低32位的异或与的结果
- Float
- return floatToIntBits(value);
- 直接转成int输出
- Double
- bits = doubleToLongBits(value); 类似floatToIntBits
- return (int)(bits ^ (bits >>> 32)); 类似Long.hashCode()
- String
- s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]
- s[i] is the ith character of the string; why use 31?
FAQ
1.为什么hashCode是int而不是其他数据类型?
因为在Java中,一个Array的最大长度是Integer.MAX_VALUE()
2.为什么Object class会有这个hashCode() ?Object 的 hashCode() 常用在什么地方?
hashCode()的本意是一个用来唯一标识object的一个code,可以把它当作加密后的密文。常用在Java自带的HashMap或HashTable的data structure 中。
equals()方法
作用
就是用于比较两个对象是否相等。我们知道所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较两个对象的的内存地址,所以说使用Object的equals()方法是比较两个对象的内存地址是否相等。
设计规则
- 自反性
- 对于任何非空引用值 x,x.equals(x) 都应返回 true。
- 对称性
- 对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
- 传递性
- 对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
- 一致性
- 对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
- 对于任何非空引用值 x,x.equals(null) 都应返回 false。
比较方法选择
1.对象域使用equals()方法
2.类型安全的枚举使用equals()或者==
3.可能为null的对象使用equals()或者==
4.数组使用Arrays.equals()
5.除了float和double外的原始数据类型使用==
6.float类型: 使用Float.foatToIntBits转换成int类型,然后使用==
7.double类型: 使用Double.doubleToLongBit转换成long类型,然后使用==