前言
List 有多个实现,本文以ArrayList(LinkedList也一样)作为说明,equals是Object的一个成员函数,例子中的bean重写实现它。
一、Bean 类定义并重写equals函数
public class Book {private String id;private String name;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic boolean equals(Object obj) {//这里重写if (obj instanceof Book) {//obj是book对象,使用当前对象id与obj的id进行对比return id == null ? false : id.equals(((Book) obj).id);} else if (obj instanceof String) {//目的可以接受id进来匹配是不是同一本书(我们得到id,不需要构造一个book对象来进行匹配对比)return obj.equals(id);}return false;}
}
二、equals的演练
- Bean 对象 vs Bean对象
public static void main(String[] args) {Book book1 = new Book();book1.setId("111");book1.setName("语文");Book book2 = new Book();book2.setId("222");book2.setName("数学");println("book1 equals book2 ? " + book1.equals(book2));
}
book1 equals book2 ? false
book2作为参数,book1对象调用equals函数,id不一样,结果自然是false。
- Bean对象 vs String 对象
public static void main(String[] args) {String id = "111";Book book1 = new Book();book1.setId(id);book1.setName("语文");println("book1 equals String ? " + book1.equals("111"));
}
book1 equals String ? true
字符串"111"作为参数,book1对象调用equals函数,结果是true。这也符合我们重写equals的目的。
List演练
我们知道,list中包含某个对象,是通过遍历list的每一个元素和给定的对象相匹配,如果匹配上说明包含,反之不包含,且匹配也是调用equals函数。
看如下代码:
public static void main(String[] args) {String id = "111";Book book1 = new Book();book1.setId(id);book1.setName("语文");Book book2 = new Book();book2.setId("222");book2.setName("数学");ArrayList list = new ArrayList(2);list.add(book1);list.add(book2);println("list contains book1 ? " + list.contains(book1));println("list contains id 111 ? " + list.contains("111"));
}
执行结果:
list contains book1 ? true
list contains id 111 ? false
结果不是我们期望的,前面已经验证 book1.equals(“111”)的结果是true,list.contains(“111”)的结果是false。
不急,来看看ArrayList的contains
实现(contains实际上是调用了indexOf函数):
public boolean contains(Object o) {return indexOf(o) >= 0;}/*** Returns the index of the first occurrence of the specified element* in this list, or -1 if this list does not contain the element.* More formally, returns the lowest index <tt>i</tt> such that* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,* or -1 if there is no such index.*/public int indexOf(Object o) {if (o == null) {for (int i = 0; i < size; i++)if (elementData[i]==null)return i;} else {for (int i = 0; i < size; i++)//因为这个判断用的是o.equals,所以不包含111if (o.equals(elementData[i]))return i;}return -1;}
for (int i = 0; i < size; i++)
确实是遍历了所有元素。问题在于if (o.equals(elementData[i]))
这里调用的是参数对象的equals,不是调用元素对象的equals函数。如果反过来if (elementData[i].equals(o))
那么我们上面的结果就是true。
通俗的讲,list.contains(“111”),contains函数中匹配时,是"111".equals(elementData[i]),不再是book.equals(“111”)。
总体来说,我们有重新实现equals的情况下,在使用list的时候要避免使用非本类的对象匹配方式,一定要同类如:list.contains(book1)。上面的情况请用111 构造出一个Book对象才能得到正确的结果。当然,list的indexOf也是如此。
那么代码上为什么有这样的坑呢,应该为了代码简练,如果写成elementData[i].equals(o)的话,elementData[i]需要进行判空。
有知道更多原因的请留言,抱拳谢过。