前言
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;
}
@Override
public 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,所以不包含111
if (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]需要进行判空。
有知道更多原因的请留言,抱拳谢过。