天天看點

List的removeAll()方法與equals()方法之坑

最新在項目中遇到一個坑,大概如下:

public class MainTest {

    public static void main(String[] args) {
        //建立一個List1對象,添加四個元素
        List<Operator> list1 = new ArrayList<>();
        Operator op1 = new Operator(, "first operator", "1");
        Operator op2 = new Operator(, "second operator", "2");
        Operator op3 = new Operator(, "second operator", "2");
        Operator op4 = new Operator(, "second operator", "3");
        list1.add(op1);
        list1.add(op2);
        list1.add(op3);
        list1.add(op4);

        //再建立另一個list2對象,添加一個與之前list1添加過的元素屬性相同的元素,
        Operator op = new Operator(, "second operator", "2");
        List<Operator> list2 = new ArrayList<>();
        list2.add(op);

        //從list1中删掉list2
        list1.removeAll(list2);
    }
}
           

其中Operator是我自定義的類。我的本意是,從從list1中删掉list2後,list1中應該隻剩下兩個元素:op1和op4了吧,可是結果卻不是這樣,而是隻剩下op1了。

這是怎麼回事呢?查找半天才發現,原因出在Operator類的實作上:

class Operator{
    int id;
    String msg;
    String extra;

    public Operator(int id, String msg, String extra) {
        super();
        this.id = id;
        this.msg = msg;
        this.extra = extra;
    }

    @Override
    public String toString() {
        return "Operator [id=" + id + ", msg=" + msg + ", extra=" + extra + "]";
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Operator other = (Operator) obj;
        if (id != other.id)
            return false;
        if (msg == null) {
            if (other.msg != null)
                return false;
        } else if (!msg.equals(other.msg))
            return false;
        return true;
    }

}
           

具體原因就是我重寫了Operator類的equals()方法,而在此方法中,我隻比較了Operator對象的id和msg屬性,而并未比較extra屬性。

而重寫equals()方法和removeAll()有什麼關系呢?看源碼:

removeAll()的方法實作在java.util.AbstractCollection類中:

public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }
           

其中調用了contains()方法,一塊貼上源碼:

public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))//這裡調用了equals()方法
                    return true;
        }
        return false;
    }
           

List的removeAll()方法會最終會調用equals()方法判斷需要删除哪些元素!

在此例中,removeAll()傳入的參數list2含有元素op,op會和list1中的元素逐個用equals()進行比較,結果,因為equals()方法不比較extra屬性,是以op 和 op2, op3, op4的比較結果都是相同的,結果list1就删的隻剩下了op1了。

如果把Operator類中重寫的equals()方法去掉,再運作,會是什麼結果呢?

去掉的話,Operator類就是Object類預設的equals()方法,會用”==”進行對象的比較,也就是比較位址。是以op和list1中的任意一個元素比較都是不相等的。結果就是,list1中不會删除任何元素。