今天遇到一件怪事,用一個ArrayList添加了一個對象,再調用ArrayList的remove方法刪除該對象,當然這時對象是數據庫里查出來的,但內容絕對是一樣,卻發現remove失敗了。演示一下,這里用了自定義的Merchant對象,測試時只需隨便自定義一個對象即可:
public static void main(String[] args) { List<Merchant> merchants = new ArrayList<>(); Merchant merchant = new Merchant(); merchant.setMerchantId("aa"); merchant.setMarketPlaceId("bb"); merchant.setSecretKey("aa"); merchant.setAccessKeyId("bb"); merchants.add(merchant); Merchant other = new Merchant(); other.setMerchantId("aa"); other.setMarketPlaceId("bb"); other.setSecretKey("aa"); other.setAccessKeyId("bb"); boolean isRemoved = merchants.remove(other); System.out.println(isRemoved); }
結果打印出來是false。然后去看了一下ArrayList的remove方法:
public boolean remove(Object var1) { int var2; if (var1 == null) { for(var2 = 0; var2 < this.size; ++var2) { if (this.elementData[var2] == null) { this.fastRemove(var2); return true; } } } else { for(var2 = 0; var2 < this.size; ++var2) { if (var1.equals(this.elementData[var2])) { this.fastRemove(var2); return true; } } } return false; } private void fastRemove(int var1) { ++this.modCount; int var2 = this.size - var1 - 1; if (var2 > 0) { System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var2); } this.elementData[--this.size] = null; }
關鍵在標黃那一行,remove的前提是兩個對象的equals方法必須相等,而我定義的Merchant對象並無復寫Object類的equals方法,那么執行到這一行時equals就是Object的equals方法:
public boolean equals(Object obj) { return (this == obj); }
是否一下子有種恍然大悟的被坑的感覺?反正我是這樣子覺得的,Object類比較的是引用,而main里測試的是兩個不同的引用,equals必然是false,remove自然也是false了,不信可以把equals也一並打印一下:
public static void main(String[] args) { List<Merchant> merchants = new ArrayList<>(); Merchant merchant = new Merchant(); merchant.setMerchantId("aa"); merchant.setMarketPlaceId("bb"); merchant.setSecretKey("aa"); merchant.setAccessKeyId("bb"); merchants.add(merchant); Merchant other = new Merchant(); other.setMerchantId("aa"); other.setMarketPlaceId("bb"); other.setSecretKey("aa"); other.setAccessKeyId("bb"); boolean isRemoved = merchants.remove(other); System.out.println(other.equals(merchant)); System.out.println(isRemoved); }
你會發現兩個false出現了。那么咋辦呢?很簡單,既然是equals引起的,那就讓它來解決問題,我們再Merchant里重寫Object的equals即可。當然既然重寫了equals,我們也得把hashCode方法也一起重寫一下,現在的IDE都很智能,自動生成即可。等你的自定義類生成好了這倆方法后,重新執行上面這個main,你會發現出現倆true了。
當然,如果你希望在自定義的對象里修改了非關鍵屬性(比如上面的Merchant里的accessKeyId、secretKey等這些屬性,在數據庫update他們的值之后就都改變了)都不影響該對象的唯一性,那么我們可以把equals和hashCode方法里自動生成的其他屬性去掉,只保留能識別該對象的關鍵屬性(比如merchantId)。