前言部分
Set 去重相信大家一定不陌生,尤其是在 Set、Set 等等,但是在使用 Set
所以想要 Set
如下以 User 实体为例,进行测试。
代码部分
测试代码:
public static void main(String[] args) { Set
打印结果:
name: 张三, age:20 name: 张三, age:10
实体对象(User.java): 重写了 equals()、hashCodd() 方法。
public class User { public User(String name, Integer age){ this.name = name; this.age = age; } /** 姓名 **/ private String name; /** 年龄 **/ private Integer age; 省略get、set方法... /** * 重写equals方法,如果对象类型是User,先比较hashcode,一致的场合再比较每个属性的值 */ @Override public boolean equals(Object obj) { System.out.println("调用equals方法,当前的hashCode为:"+hashCode()); /** 对象是 null 直接返回 false **/ if (obj == null) { return false; } /** 对象是当前对象,直接返回 true **/ if (this == obj) { return true; } /** 判断对象类型是否是User **/ if (obj instanceof User) { User vo = (User) obj; /** 比较每个属性的值一致时才返回true **/ /** 有几个对象就要比较几个属性 **/ if (vo.name.equals(this.name) && vo.age.equals(this.age)) { return true; } } return false; } /** * 重写hashcode方法,返回的hashCode一样才再去比较每个属性的值 */ @Override public int hashCode() { return this.getName().hashCode() * this.getAge().hashCode(); } }
解释部分
为什么 Set、Set 就可以直接实现去重,而 Set
大家对这个问题有过疑惑吗?
1、HashSet 添加数据过程
HashSet 的底层实现,相信大家都清楚是 HashMap 吧?我们在 add() 数据时,其实一层层找,最终是调的 HashMap 的 put() 方法,如下是 HashSet 的 add() 方法,其中 map 为 HashMap。
我们再点一层找到 HashMap 的 put() 方法:
如上图所示,通过 putVal() 方法我们大致有了个概念了,判断是否为旧值就是对 hash 值、key 值进行比较。
hash 值比较自然调用的事 hashCode() 方法,而 key 值的比较实用的是 equals() 方法。
了解到这基本就可以看出 hashCode() 、equals() 方法对于去重的重要性了。
2、Set
那么接下来我们就可以来看看 Set
我们以 String 为例,假设有两个字符串 a、b,如下:
String a = "123"; String b = "123"; System.out.println("a.hashCode:"+a.hashCode()); System.out.println("b.hashCode:"+b.hashCode()); System.out.println(a.equals(b));
打印结果如下:
a.hashCode:48690 b.hashCode:48690 true
很显然,在没有重写 hashCode() 、equals() 方法时,字符串 a、b 的 hashCode,equalse() 是一致的,那么这两个就可以视为一个对象,所以用在 Set 里面就可以直接去重。
但是为什么会一致呢?
任何对象在不重写 equals()、hashcode() 的情况下,使用的是 Object 对象的 equals() 方法和 hashcode() 方法,而重点就是,默认的 equals() 方法判断的是两个对象的引用指向的是不是同一个对象;而 hashcode 也是根据对象地址生成一个整数数值;
显然字符串 a、b 这两个条件都满足,所以对于 Set 来说就是一个对象的概念。
3、Set
但是换到对于实体对象就行不通了,我们再来套 Object 的 equals()、hashCode() 方法。
当我们 new User() 对象时,两个对象的地址引用肯定是不同的;其次 hashcode 是根据对象地址生成的,这样显然也不同,所以对于 Set 来说,那么去重就行不通。
因此,想要让 Set
只有两个对象的 hashCode() 方法的值一致,且 equalse() 方法返回 true,那么这对于 Set
equales()重写,hashCode()不重写
@Override public boolean equals(Object obj) { return true; } //@Override //public int hashCode() { // return this.getName().hashCode() * this.getAge().hashCode(); //}
执行代码:
Set
打印内容:
name: 张三, age:10 name: 张三, age:10
equales()不重写,hashCode()重写
//@Override //public boolean equals(Object obj) { // return true; //} @Override public int hashCode() { return this.getName().hashCode() * this.getAge().hashCode(); }
执行代码+打印内容如上:
name: 张三, age:10 name: 张三, age:10
总结
总之,要想保证 Set
- 重写 hashCode() 方法,确保两者 hashcode 一致,比如使用属性相乘或者相加。
- 重写 equals() 方法,相同对象、属性值相同对象皆为相等。
通过上面这些例子也能看出重写 equals 方法,就必须重写 hashCode 的重要性,因为只重写 equals() 不一定能满足预期相等的效果。
如下是阿里巴巴开发手册,关于 hashCode 和 equals 的处理规则: