原文:https://blog.csdn.net/freelander_j/article/details/52211010
在java中,要將一個集合中重復的對象除去,如果這個集合中的數據類型是基本數據類型,可以直接將list集合轉換成set,就會自動去除重復的元素,大家都知道set集合的特點就是沒有重復的,這個就相對比較簡單,這里不在詳細說,我們下面說的list集合中的數據類型是一個對象類型的。
當list集合中存儲的類型是對象類型的時候,我們就不能簡單的只把list集合轉換成set集合就行了,這時我們需要在對象的實體類中去重寫equals()方法和hashCode()方法,我們以一個list集合為例,在該例中,我們將People實體類中姓名和電話號碼作為判斷該對象重復的標識,在People的實體類中我們重寫這兩個方法如下:
-
public class People {
-
-
private String name;
-
private int id;
-
private String phoneNumber;
-
private int age;
-
private String introduce;
-
-
public People(String name, int id, String phoneNumber, int age,
-
String introduce) {
-
super();
-
this.name = name;
-
this.id = id;
-
this.phoneNumber = phoneNumber;
-
this.age = age;
-
this.introduce = introduce;
-
}
-
// ....... 這里省略getter和setter方法
-
-
-
public boolean equals(Object arg0) {
-
// TODO Auto-generated method stub
-
People p = (People) arg0;
-
return name.equals(p.name) && phoneNumber.equals(p.phoneNumber);
-
}
-
-
-
public int hashCode() {
-
// TODO Auto-generated method stub
-
String str = name + phoneNumber;
-
return str.hashCode();
-
}
-
-
}
以上實體類中,我們在equals()方法中取出該對象的name和phoneNumber這兩個屬性值去判斷比較,然后在重寫的hashCode()方法中返回這兩個屬性值得hashCode值。
-
public class Test {
-
-
public static void main(String[] args) {
-
-
List<People> listPeople = new ArrayList<People>();
-
listPeople.add( new People("張三", 1, "13355556666", 23, "新員工"));
-
listPeople.add( new People("張三", 2, "15522223333", 23, "老員工"));
-
listPeople.add( new People("李四", 3, "13355556666", 23, "實習生"));
-
listPeople.add( new People("提莫", 4, "13311112222", 23, "經理"));
-
listPeople.add( new People("張三", 5, "13355556666", 23, "會計"));
-
listPeople.add( new People("德瑪", 6, "3344", 23, "開發"));
-
listPeople.add( new People("卡特", 7, "13355556666", 23, "測試"));
-
listPeople.add( new People("提莫", 8, "13355556666", 23, "美工"));
-
listPeople.add( new People("提莫", 9, "13311112222", 23, "實施"));
-
listPeople.add( new People("卡茲克", 10, "13356786666", 23, "售前"));
-
listPeople.add( new People("亞索", 11, "13355556666", 23, "銷售"));
-
-
Set<People> setData = new HashSet<People>();
-
setData.addAll(listPeople);
-
-
System.out.println( "list- size----" + listPeople.size());
-
System.out.println( "list-----" + listPeople.toString());
-
-
System.out.println( "set- size----" + setData.size());
-
System.out.println( "set-----" + setData.toString());
-
-
for(People pp : setData) {
-
System.out.println( "p--" + pp.toString());
-
}
-
-
}
-
-
-
}
運行這段代碼之后,我們就會發現,在原來的list集合中姓名和電話號碼都相同的對象就被會認為是重復的元素而刪除掉,很明顯運行結果已經達到我們的目的。
這里需要說一下equals()方法和hashCode()方法,一般情況下我們重寫equals()方法的時候都要去重寫hashCode()方法,這是為什么呢?大家不妨可以這樣去試一試上面那個例子,在實體類中將重寫的hashCode()方法注釋掉,再去運行該程序,這時就會發現運行結果並不是我們剛剛得到的結果,在set集合中,並沒有將我們認為是重復的元素刪除掉,下面我們通過這兩個方法的源碼去了解一下:
String類中的equals()方法的源碼如下
-
public boolean equals(Object anObject) {
-
if (this == anObject) {
-
return true;
-
}
-
if (anObject instanceof String) {
-
String anotherString = (String)anObject;
-
int n = count;
-
if (n == anotherString.count) {
-
char v1[] = value;
-
char v2[] = anotherString.value;
-
int i = offset;
-
int j = anotherString.offset;
-
while (n-- != 0) {
-
if (v1[i++] != v2[j++])
-
return false;
-
}
-
return true;
-
}
-
}
-
return false;
-
}
通過觀察equals()方法的源碼我們可以看出,該方法去比較兩個對象時,首先先去判斷兩個對象是否具有相同的地址,如果是同一個對象的引用,則直接放回true;如果地址不一樣,則證明不是引用同一個對象,接下來就是挨個去比較兩個字符串對象的內容是否一致,完全相等返回true,否則false。
String類中hashCode()方法的源碼如下
-
public int hashCode() {
-
int h = hash;
-
if (h == 0 && count > 0) {
-
int off = offset;
-
char val[] = value;
-
int len = count;
-
-
for (int i = 0; i < len; i++) {
-
h = 31*h + val[off++];
-
}
-
hash = h;
-
}
-
return h;
-
}
以上是String類中重寫的hashCode()方法,在Object類中的hashCode()方法是返回對象的32位JVM內存地址,也就是說如果我們不去重寫該方法,將會返回該對象的32位JVM內存地址,以上我們測試的例子中,當注釋掉重寫的hashCode()方法時,這時默認返回對象的32JVM中的地址,兩個不同的對象地址顯然是不同的,我們在比較時,雖然通過重寫的equals()方法比較出來name和phoneNumber值是相同的,但是默認的hashCode()方法返回的值他們並不是同一個對象,所以我們通常要將hashCode()方法與equals()方法一起重寫,以維護hashCode方法的常規協定,該協定聲明相等對象必須具有相等的哈希碼。
曾在網上一篇博客中看到過這樣的解釋,用白話說,通過hashCode判斷對象是否放在同一個桶里,然后再通過equals方法去判斷這個桶里的對象是不是相同的,這個比喻也挺形象的。
關於hashCode方法的作用大家可以看看這篇博客,講的很清楚:http://blog.csdn.net/fenglibing/article/details/8905007