剖析Java中在Collection集合中使用contains和remove為什麼要重寫equals

引言

在Collection集合中:
contains方法是判斷一個集合裡面是否包含指定元素,如果有則返回true;
remove方法是從集合中刪除指定元素的單個實例;
這兩個方法看起很簡單,用起來也很簡單,同樣也非常常用;但是,它們到底是怎麼匹配到相應的元素呢?

源碼剖析

以ArrayList為例,我們分析一下ArrayList中的contains和remove的源碼;

先看看contains:

在這裡插入圖片描述

這裡看到比較的對象是一個Object類,變量名為 o(就是是否包含 o ),並且調用瞭一個indexOf方法,接下來我們進一步看看indexOf源碼:

在這裡插入圖片描述

可以看到,indexOf又進一步調用瞭indexOfRange方法,我們還需要深入看看這個方法:

在這裡插入圖片描述

這裡可以發現,indexOfRange中 o 調用瞭equals方法(藍色部分)!
我們知道:equals方法是判斷兩個對象是否相等,但是默認情況下比較的是對象的地址,如果想要比較對象的內容就需要重寫equals方法;
那麼這個contains調用瞭equals方法,所以,contains判斷一個集合中是否包含某個元素其實就是通過對象地址比較的瞭;
這並不是我們想要的結果,所以幾乎所有放在集合中的類型,都需要重寫equals方法!

為什麼是幾乎所有?
因為還是有特例的:SUN公司已經把String類和包裝類的equals方法重寫瞭,所以對於這兩種我們不需要重寫equals!

同樣看看remove方法:

在這裡插入圖片描述

同樣,remove方法也是通過equals方法比較元素然後移除的;

所以這裡可以得出一個結論:
Collection集合中的remove方法和contains方法底層都會調用equals,所以隻要放在集合中的類型,都要重寫equals方法;
因為對對象的地址的比較沒有什麼意義,我們實際上需要的是對象內容間的比較;

實例測試

知道瞭結論,就來寫幾個代碼測試一下:

String類和包裝類的特殊情況

對於 String類型和包裝類,SUN公司重寫瞭equals方法,所以我們先測試一下這兩種情況:

import java.util.ArrayList;
import java.util.Collection;

// 結論:Collection接口中的remove方法和contains方法底層都會調用equals,
// 所以存放在一個集合中的類型,要重寫它的equals方法
// (但是String和包裝類的equals方法已經重寫過瞭,不用重寫)
public class CollectionTest02 {
    public static void main(String[] args) {
        // 這裡以ArrayList為例
        Collection array1 = new ArrayList();

        // String類:
        String s1 = "Hello";
        // 將s1放入array1
        array1.add(s1);
        // 定義一個s2也為 "Hello",那麼調用contains是否會包含s2?
        String s2 = "Hello";
        System.out.println("array1是否包含s2?" + array1.contains(s2)); // true
        // 因為array1中放入的是s1,如果移除s2,s1會不會被移除呢?
        array1.remove(s2);
        System.out.println("移除s2後s1是否還在array1中?" + array1.contains(s1)); // false


        // 包裝類也同樣:
        Integer num1 = 1000;
        // 將num1放入array1
        array1.add(num1);
        // 定義一個num2也為1000
        Integer num2 = 1000;
        System.out.println("array1是否包含num2?" + array1.contains(num2)); // true
        // 移除num2觀察num1是否會被移除
        array1.remove(num2);
        System.out.println("移除num2後num1是否還在array1中?" + array1.contains(num1)); // false
    }
}

輸出結果:

array1是否包含s2?true
移除s2後s1是否還在array1中?false
array1是否包含num2?true
移除num2後num1是否還在array1中?false

自定義類型

這是equals重寫的情況,接下來我自定義一個類型,看看沒有重寫會發生什麼;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionTest03 {
    public static void main(String[] args) {
        // 還是以ArrayList為例
        Collection array = new ArrayList();

        // 創建一個User對象u1
        User u1 = new User("張三");
        // 將u1對象放入array中
        array.add(u1);
        // 再創建一個User對象,使其內容和u1相同都為"張三",那麼調用contains是否會包含該對象呢?
        System.out.println("array中是否包含新創建的對象?" + array.contains(new User("張三"))); // false
        // 移除一個內容為"張三"的新的對象,u1是否會被移除?
        array.remove(new User("張三"));
        System.out.println("移除後u1是否存在?" + array.contains(u1)); // true
    }
}

// 自己定義一個User類
class User {
    // 成員變量:姓名
    private String name;

    // 默認構造
    User() {}
    // 有參構造:初始化姓名
    User(String name) {
        this.name = name;
    }
}

輸出結果:

array中是否包含新創建的對象?false
移除後u1是否存在?true

可以看到,我自定義的User方法裡沒有重寫equals方法,所以當調用contains和remove時,雖然傳入的對象內容和u1的對象內容相同都為“張三”,但是實際上比較的卻是對象的地址;

接下來我重寫User的equals方法,看看結果如何;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionTest03 {
    public static void main(String[] args) {
        // 還是以ArrayList為例
        Collection array = new ArrayList();

        // 創建一個User對象u1
        User u1 = new User("張三");
        // 將u1對象放入array中
        array.add(u1);
        // 再創建一個User對象,使其內容和u1相同都為"張三",那麼調用contains是否會包含該對象呢?
        System.out.println("array中是否包含新創建的對象?" + array.contains(new User("張三"))); // true
        // 移除一個內容為"張三"的新的對象,u1是否會被移除?
        array.remove(new User("張三"));
        System.out.println("移除後u1是否存在?" + array.contains(u1)); // false
    }
}

// 自己定義一個User類
class User {
    // 成員變量:姓名
    private String name;

    // 默認構造
    User() {}
    // 有參構造:初始化姓名
    User(String name) {
        this.name = name;
    }

    // 重寫equals方法 ,通過name進行比較
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return name.equals(user.name);
    }
}

輸出結果:

array中是否包含新創建的對象?true
移除後u1是否存在?false

我隻是重寫瞭一個equals方法,其他地方都沒變,結果完全不同;

所以這就驗證瞭之前的結論:contains 和 remove 底層實現都調用瞭equals方法;

總結

其實這篇文章就是分析一下contains 和 remove 底層實現,主要想說的還是那一句話:
Collection集合中的remove方法和contains方法底層都會調用equals,所以隻要放在集合中的類型,都要重寫equals方法;(String和包裝類除外)

希望各位在Java學習中養成好習慣,要時刻惦記著equals的重寫,不然如果做瞭一個項目你連錯在哪裡都不好找到;

到此這篇關於剖析在Collection集合中使用contains和remove為什麼要重寫equals的文章就介紹到這瞭,更多相關Collection 重寫 equals內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: