更新于 2016年06月01日
如果 Object o 非 null,那么 o.equals(null) 恒等于 false,即 null 不等于任何非 null 对象。
====================
更新于 2016年01月25日,添加 ArrayList.remove(Object ojb) 部分。
==================
首先,equals()是个方法,在祖先类Object中已经实现,源代码如下:
public boolean equals(Object obj) {
return (this == obj);
}
== 是个运算符,表示内存地址是否相同。从Object类中equals的实现可以看出,原始的equals方法跟==是等价的。
先对Java中的各种数据类型进行一一说明。
基本数据类型
如 byte, char, short, int, long, float, double, boolean等基本数据类型,== 就是表示数量相等,由于不是对象,所以不能使用equals()方法,但是其Integer, Float类可以使用。
对象
如Integer,String和用户自定义的类,== 表示其内存地址是否相同。
其中,String是一个特例,具体见如下代码:
String foo = "loveu";
String bar = "loveu";
System.out.println(foo == bar); // 输出为 true
System.out.println(foo.equals(bar)); // 输出为 true
// String foo = new String("loveu");
// String bar = new String("loveu");
// System.out.println(foo == bar); // 输出为 false
// System.out.println(foo.equals(bar)); // 输出为 true
上述代码中,第1段中,2个的"loveu"是字符串常量,存放在常量池里,共用1块内存空间,所以内存地址相同,== 结果为true,String类里的equals方法已经被重写,只要内容相同,返回就为true(具体请参见其源代码),所以equals的结果也为true;第2段代码中,新建2个字符串对象,内存地址不同,内容相同,故结果分别为false和true。
自定义类
要重写equals方法,有2种方法实现:
- 如果使用Eclipse,在自定义类右击——Source——Generate hashCode() and equals(),选择该类相应的成员作为标准,即可生成equals()和hashCode()(hashCode()和equals()是成对出现的,一定要同时重写,同时要重写hashCode()是可以保证对象的功能兼容于hash集合。这是一个好习惯,即使这些对象不会被存储在hash集合中。);
- 自定义equals(),根据具体业务需要,编写判重标准(注意:因为是重写,equals()的形参一定保持为Object类型,否则重写无效,可以用 @override 来修饰重写方法,如果重写不合法,编译时会报错);
举个列子,自定义类News,有成员变量url,2个url相同的News对象,认为是相等的。使用上述两种重写equals的方式,可得代码:
方式1
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((url == null) ? 0 : url.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
VolatileTest other = (VolatileTest) obj;
if (url == null) {
if (other.url != null)
return false;
} else if (!url.equals(other.url))
return false;
return true;
}
方式2
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 100;
int result = 1;
result = prime * result + ((url == null) ? 0 : url.hashCode());
return result;
}
/**
* 判断2条新闻是否相等
* @param o
* @return
*/
@Override
public boolean equals (Object o) {
if (o == null || (o instanceof News) == false) {
return false;
} else {
News n = (News) o;
return url.equals(n.getUrl());
}
}
应用举例
@Override public boolean remove(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
}
return false;
}
如上,在遍历寻找过程中,是通过equals()来判断相等的,所以如果 object 是自定义类型,如果不重写 equals() 方法,则删除是同一个(即内存地址相同)的对象;如果要实现删除内存地址不同而只是内容相同(判断是否相同的标准在 equals()方法中定义)的对象,则一定要重写 equals()方法,否则将会出现误删或内容明明相同却无法删除的情况。
而且,在使用ArrayList.remove(Object o)时不必再进行遍历,直接调用就行。即要么是:
<pre name="code" class="java">for (int i = 0; i < list.isze(); i++) {
if (list.get(i).id == obj.id) {
list.remove(i);
break;
}
}
要么:
list.remove(obj);
而不要多此一举,因为如上所示 jdk 源码里已经做了遍历:
<pre name="code" class="java">for (int i = 0; i < list.isze(); i++) {
if (list.get(i).id == obj.id) {
list.remove(obj);
break;
}
}
ArrayList.contains(Object o)
除此之外,contains方法也是通过equals实现的,以ArrayList为例,具体源代码如下:
/**
* Returns <tt>true</tt> if this list contains the specified element.
* More formally, returns <tt>true</tt> if and only if this list contains
* at least one element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
* @return <tt>true</tt> if this list contains the specified element
*/
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++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
所以,当contains()的参数是自定义类而且内容相同、内存地址不同仍视为“相同”时,必须重写该类的equals()。