最近遇到个坑,在分别对ArrayList、HashMap等数据类型进行比较时,发现数据一样,但equals一直返回false。于是乎看了一下ArrayList和HashMap的源码,才恍然大悟。本文的代码摘自JDK 1.7.0。
ArrayList的equals方法:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator e2 = ((List) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
可以看出,equals方法内部在对列表元素进行逐个比较时,调用了元素的equals方法,故需要根据需求对列表元素的equals方法进行重写。
HashMap的equals方法:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map))
return false;
Map<K,V> m = (Map<K,V>) o;
if (m.size() != size())
return false;
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
可以看出,当比较Map的数据时,equals方法对Key和Value同时进行了比较。对于Key的比较调用了HashMap.containsKey(Object)方法,该方法最终调用到了getEntry(Object)方法,代码如下:
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
该方法通过key的hashCode来获取一个hash值,进而从HashMap内部的table中获取该key对应的对象。对于value的比较,是通过调用value的equals来进行比较的。故人们常说,如果你为某个类编写了equals方法,那么应该同时编写hashCode方法,难道就是为了让HashMap进行比较时不会出错?
HashSet的equals方法,位于AbstractSet中:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
查看containsAll的代码:
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
最终调用了HashMap的contains方法:
public boolean contains(Object o) {
return map.containsKey(o);
}
其内部的map为一个HashMap类型,有HashMap的equals方法的实现可知,HashSet进行比较时,用到了对象的hasCode和equals方法。
小结
ArrayList的equals方法只用到数据项的equals方法,而HashMap和HashSet用到了数据项的hashCode和equals方法