最近遇到个坑,在分别对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方法
时间: 2024-10-19 18:54:42