笔者文笔功力尚浅,若有不妥,请慷慨指出,一定感激涕零java
在Effective Java
中第九条规定在覆盖equals()
方法时总要覆盖hashCode()
方法。这是为何呢?接下来咱们就介绍一下这两个方法。git
Java中的equals()
方法和hashCode()
方法都是在Object
类中的方法,而在Java中全部的类都是Obejct
类的子类,因此Java中全部的方法都会有这两个方法的默认实现。github
Object
类中的equals()
方法定义以下web
1public boolean equals(Object obj) {
2 return (this == obj);
3}
复制代码
咱们发如今equals()
方法中就关键的==
,那么==
在Java中有什么含义呢,咱们都知道在Java中分为基本数据类型和引用数据类型。那么==
在这两个类型中做用是不同的。面试
==
两边值是否相等==
两边内存地址是否相等基本数据类型包括:
byte
,short
,char
,int
,long
,float
,double
,boolean
算法
而经过Java文档中的equals()
方法描述,全部要实现本身的equals()
方法都要遵照下面几个规则后端
x.equals(x)
应该返回true
x.equals(y)
返回true
,那么y.equals(x)
也应该返回true
x.equals(y)
返回true
,y.equals(z)
返回true
,那么y.equals(z)
也应该返回true
x.equals(null)
都应该返回false
Object
中的hashCode()
方法是一个本地方法,返回一个int
类型的哈希值。数组
1public native int hashCode();
复制代码
在hashCode()
方法中也有一些规约app
equals
方法中进行比较的参数没有修改,那么屡次调用一个对象的hashCode()
方法返回的哈希值应该是相同的。equals
方法比较是相等的,那么要求这两个对象的hashCode
方法返回的值也应该是相等的。equals
方法比较是不一样的,那么也不要求这两个对象的hashCode
方法返回的值是相同的。可是咱们应该知道对于不一样对象产生不一样的哈希值对于哈希表(HashMap等等)可以提升性能。这两个方法常常出如今Java中的哪一个类里面呢?若是看过HashMap
源码的应该了解这两个方法常常出如今HashMap
中。网上介绍HashMap
类的文章有不少了,这里就简单介绍一下HashMap
。框架
当一个节点中的链表超过了8的时候就会变为红黑树,以解决链表长度过长之后查询速度慢的缺点。
HashMap
是由数组和链表组成的高效存储数据的结构。那么是如何肯定一个数据存储在数组中的哪一个位置呢?就是经过hashCode
方法进行计算出存储在哪一个位置,还记得咱们上面讲hashCode
方法说了有可能两个不一样对象的hashCode
方法返回的值相同,那么此时就会产生冲突,产生冲突的话就会调用equals
方法进行比对,若是不一样,那么就将其加入链表尾部,若是相同就替换原数据。
计算位置固然不是上面简单的一个
hashCode
方法就计算出来,中间还有一些其余的步骤,这里能够简单的认为是hashCode
肯定了位置。
若是你不将自定义的类定义为HashMap
的key值的话,那么咱们重写了equals
方法而没有重写hashCode
方法,编译器不会报任何错,在运行时也不会抛任何异常。
若是你想将自定义的类定义为HashMap
的key值得话,那么若是重写了equals
方法那么就必须也重写hashCode
方法。
接下来咱们能够看一下咱们使用自定义的类做为HashMap
的key,而且自定义的类不重写equals
和hashCode
方法会发生什么。
自定义的类
1@Builder
2@NoArgsConstructor
3@AllArgsConstructor
4class CustomizedKey{
5 private Integer id;
6 private String name;
7}
复制代码
接下来咱们看使用自定义的类做为key
1 public static void main(String[] args) {
2
3 Map<CustomizedKey, Integer> data = getData();
4
5 CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
6
7 Integer integer = data.get(key);
8
9 System.out.printf(String.valueOf(integer));
10 }
11
12 private static Map<CustomizedKey,Integer> getData(){
13 Map<CustomizedKey,Integer> customizedKeyIntegerMap = new HashMap<>();
14 CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
15 customizedKeyIntegerMap.put(key,10);
16 return customizedKeyIntegerMap;
17 }
复制代码
咱们能够看到程序最后打印的是一个null
值。缘由正如上面咱们说的同样。
hashCode
:用来计算该对象放入数组中的哪一个位置,由于是两个都是new的对象,因此即便里面的值同样,可是对象所处的地址却不一样,因此使用默认的hashCode
也就不一样,固然在hashMap
中就不会认为两个是一个对象。接下来咱们就重写一下这两个方法。若是咱们使用IDEA
的话,那么直接使用快捷键便可。
接下来咱们看咱们实现的两个方法
1@Builder
2@NoArgsConstructor
3@AllArgsConstructor
4class CustomizedKey{
5 private Integer id;
6 private String name;
7
8 @Override
9 public boolean equals(Object o) {
10 if (this == o) return true;
11 if (o == null || getClass() != o.getClass()) return false;
12 CustomizedKey that = (CustomizedKey) o;
13 return Objects.equals(id, that.id) &&
14 Objects.equals(name, that.name);
15 }
16
17 @Override
18 public int hashCode() {
19 return Objects.hash(id, name);
20 }
21}
复制代码
而后咱们再次运行上面的程序发现输出打印已经变成了10
。
咱们也可以使用
Lombok
提供的@EqualsAndHashCode
注解简化代码