1. 程式人生 > >List集合去除重複物件及equals()、hashCode()方法的作用

List集合去除重複物件及equals()、hashCode()方法的作用

        在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方法

	@Override
	public boolean equals(Object arg0) {
		// TODO Auto-generated method stub
		People p = (People) arg0;
		return name.equals(p.name) && phoneNumber.equals(p.phoneNumber);
	}
	
	@Override
	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方法去判斷這個桶裡的物件是不是相同的,這個比喻也挺形象的。