JAVA hashCode()和equals()方法详解

来源:互联网 发布:python config.get 编辑:程序博客网 时间:2024/06/03 08:49

在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()有可能相等,也有可能不等。

注意事项

  1. hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
  2. 如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
  3. 如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
  4. 两个对象的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类型,然后使用==

原创粉丝点击