前言
首先要了解HashSet實作的機制,如果不了解,對于下面的操作也是一直半解,用過一次就忘,并沒有什麼意義。
HashSet實作原理
HashSet的實作内部其實是一個HashMap,HashMap的實作就是根據key來進行Hash變換映射到index下,如果index相同,會根據equal來判斷key是不是相同,不相同就在這個index使用連結清單存儲,key相同就覆寫原來的值。這樣就保證了key的唯一性。HashSet就是利用HashMap key的唯一性來實作集合内的資料不重複。這就是HashSet的原理了。
對象和字元串的不同
根據上面的HashSet的實作原理,Set裡面的元素都是HashMap的Key,而在HashMap中,Key預設使用的是字元串,為什麼不使用對象呢?是因為在HashMap中判斷Key是否相等的關鍵在于equal,Object預設的equal實作是使用==來判斷兩個對象是否相等的,這樣就會導緻String和普通的Object有很大的不同。String可以使用==來判斷兩個字元串是否相等,但是對于普通對象來說==隻是判斷對象的引用是否相等。這樣一來,目标就很明确了,要儲存自定義的不重複對象,必須要重寫對象的equal方法。
規約
再根據一條很有意義的規約:
重寫equal方法必須要重寫hashCode方法。
這樣就知道我們需要做什麼了:重寫equal方法和hashCode方法。
示例
下面例子指定一個屬性,自定義具有相同該屬性的對象隻能存儲一次,實作了不重複存儲。也就是去重。
package hashSetExample;
import java.util.HashSet;
import java.util.Set;
/**
* Created by liusxg on 2017/3/9.
*/
public class RemoveDuplicateObj {
static class Test {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
//重寫equal
@Override
public boolean equals(Object var1) {
if (!(var1 instanceof Test)) {
return false;
}
Test testVal1 = (Test) var1;
if (testVal1.name == null) {
return false;
}
return this.name == testVal1.getName();
}
//重寫hashCode
@Override
public int hashCode() {
return this.name.hashCode();
}
//重寫toString,為列印友善
@Override
public String toString() {
return "{"+this.name+","+this.getAge()+"}";
}
}
public static void main(String[] args) {
//測試資料
Test test1 = new Test();
test1.setName("小明");
test1.setAge();
Test test2 = new Test();
test2.setName("小紅");
test2.setAge();
Test test3 = new Test();
test3.setName("小明");
test3.setAge();
//測試代碼
Set<Test> testSet = new HashSet<Test>();
testSet.add(test1);
testSet.add(test2);
testSet.add(test3);
System.out.println(testSet.size());
System.out.println(testSet.toString());
}
}
輸出結果:
2
[{小明,10}, {小紅,20}]
如果把equal和hashCode重寫注釋掉,輸出結果為:
3
[{小紅,20}, {小明,10}, {小明,30}]