Java中的Set对象去重
前言部分
Set<T> 去重相信大家一定不陌生,尤其是在 Set<String>、Set<Integer> 等等,但是在使用 Set<實(shí)體> ,在不重寫 equals()、hashCode() 方法情況下,直接使用貌似并不能生效。
所以想要 Set<實(shí)體> 實(shí)現(xiàn)去重,核心部分在實(shí)體中重寫 equals()、hashCode() 方法。
如下以 User 實(shí)體為例,進(jìn)行測(cè)試。
代碼部分
測(cè)試代碼:
public static void main(String[] args) {Set<User> userSet = new HashSet<User>(){{add(new User("張三",10));add(new User("張三",20));add(new User("張三",10));}};userSet.forEach(user -> {System.out.println(String.format("name: %s, age:%s",user.getName(),user.getAge()));}); }打印結(jié)果:
name: 張三, age:20 name: 張三, age:10實(shí)體對(duì)象(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方法,如果對(duì)象類型是User,先比較hashcode,一致的場(chǎng)合再比較每個(gè)屬性的值*/@Overridepublic boolean equals(Object obj) {System.out.println("調(diào)用equals方法,當(dāng)前的hashCode為:"+hashCode());/** 對(duì)象是 null 直接返回 false **/if (obj == null) {return false;}/** 對(duì)象是當(dāng)前對(duì)象,直接返回 true **/if (this == obj) {return true;}/** 判斷對(duì)象類型是否是User **/if (obj instanceof User) {User vo = (User) obj;/** 比較每個(gè)屬性的值一致時(shí)才返回true **//** 有幾個(gè)對(duì)象就要比較幾個(gè)屬性 **/if (vo.name.equals(this.name) && vo.age.equals(this.age)) {return true;}}return false;}/*** 重寫hashcode方法,返回的hashCode一樣才再去比較每個(gè)屬性的值*/@Overridepublic int hashCode() {return this.getName().hashCode() * this.getAge().hashCode();}}解釋部分
為什么 Set<String>、Set<Integer> 就可以直接實(shí)現(xiàn)去重,而 Set<實(shí)體> 就不可以,反而要重寫 equals()、hashCode() 方法才能實(shí)現(xiàn),更甚者是,只重寫 equals() 方法,而不重寫 hashCode() 方法都沒法完成去重~
大家對(duì)這個(gè)問題有過疑惑嗎?
1、HashSet 添加數(shù)據(jù)過程
HashSet 的底層實(shí)現(xiàn),相信大家都清楚是 HashMap 吧?我們?cè)?add() 數(shù)據(jù)時(shí),其實(shí)一層層找,最終是調(diào)的 HashMap 的 put() 方法,如下是 HashSet 的 add() 方法,其中 map 為 HashMap。
我們?cè)冱c(diǎn)一層找到 HashMap 的 put() 方法:
如上圖所示,通過 putVal() 方法我們大致有了個(gè)概念了,判斷是否為舊值就是對(duì) hash 值、key 值進(jìn)行比較。
hash 值比較自然調(diào)用的事 hashCode() 方法,而 key 值的比較實(shí)用的是 equals() 方法。
了解到這基本就可以看出 hashCode() 、equals() 方法對(duì)于去重的重要性了。
2、Set<單屬性> 可以直接使用去重
那么接下來我們就可以來看看 Set<單屬性>(單屬性:String、Integer等),為什么直接使用就可以去重了。
我們以 String 為例,假設(shè)有兩個(gè)字符串 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));打印結(jié)果如下:
a.hashCode:48690 b.hashCode:48690 true很顯然,在沒有重寫 hashCode() 、equals() 方法時(shí),字符串 a、b 的 hashCode,equalse() 是一致的,那么這兩個(gè)就可以視為一個(gè)對(duì)象,所以用在 Set 里面就可以直接去重。
但是為什么會(huì)一致呢?
任何對(duì)象在不重寫 equals()、hashcode() 的情況下,使用的是 Object 對(duì)象的 equals() 方法和 hashcode() 方法,而重點(diǎn)就是,默認(rèn)的 equals() 方法判斷的是兩個(gè)對(duì)象的引用指向的是不是同一個(gè)對(duì)象;而 hashcode 也是根據(jù)對(duì)象地址生成一個(gè)整數(shù)數(shù)值;
顯然字符串 a、b 這兩個(gè)條件都滿足,所以對(duì)于 Set 來說就是一個(gè)對(duì)象的概念。
3、Set<實(shí)體> 去重
但是換到對(duì)于實(shí)體對(duì)象就行不通了,我們?cè)賮硖?Object 的 equals()、hashCode() 方法。
當(dāng)我們 new User() 對(duì)象時(shí),兩個(gè)對(duì)象的地址引用肯定是不同的;其次 hashcode 是根據(jù)對(duì)象地址生成的,這樣顯然也不同,所以對(duì)于 Set 來說,那么去重就行不通。
因此,想要讓 Set<實(shí)體> 實(shí)現(xiàn)去重效果,那么就需要重寫 equals() 、hashCode() 方法。
只有兩個(gè)對(duì)象的 hashCode() 方法的值一致,且 equalse() 方法返回 true,那么這對(duì)于 Set<實(shí)體> 來說就可以看做一個(gè)對(duì)象, 如果兩者只滿足一個(gè)是不可以的(只重寫一個(gè)),舉個(gè)例子:
equales()重寫,hashCode()不重寫
@Override public boolean equals(Object obj) {return true; }//@Override //public int hashCode() { // return this.getName().hashCode() * this.getAge().hashCode(); //}執(zhí)行代碼:
Set<User> userSet = new HashSet<User>(){{add(new User("張三",10));add(new User("張三",20));add(new User("張三",10)); }};userSet.forEach(user -> {System.out.println(String.format("name: %s, age:%s",user.getName(),user.getAge())); });打印內(nèi)容:
name: 張三, age:10 name: 張三, age:10equales()不重寫,hashCode()重寫
//@Override //public boolean equals(Object obj) { // return true; //}@Override public int hashCode() {return this.getName().hashCode() * this.getAge().hashCode(); }執(zhí)行代碼+打印內(nèi)容如上:
name: 張三, age:10 name: 張三, age:10總結(jié)
總之,要想保證 Set<實(shí)體> 實(shí)現(xiàn)去重,就需要兩個(gè)實(shí)體 “一致”,這里的一致是只需要滿足如下兩個(gè)條件:
- 重寫 hashCode() 方法,確保兩者 hashcode 一致,比如使用屬性相乘或者相加。
- 重寫 equals() 方法,相同對(duì)象、屬性值相同對(duì)象皆為相等。
通過上面這些例子也能看出重寫 equals 方法,就必須重寫 hashCode 的重要性,因?yàn)橹恢貙?equals() 不一定能滿足預(yù)期相等的效果。
如下是阿里巴巴開發(fā)手冊(cè),關(guān)于 hashCode 和 equals 的處理規(guī)則:
希望這篇文章對(duì)你有所幫助。博客園持續(xù)更新,歡迎關(guān)注。
博客園:https://www.cnblogs.com/niceyoo
總結(jié)
以上是生活随笔為你收集整理的Java中的Set对象去重的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 文件转换成二进制_java实现
- 下一篇: vue使用axios并存数据到state