【Java杂记】Equals 和 hashCode
equals 和 hashCode含义
equal:判断两个对象是否相等,如果相同,返回true 否则返回false
hashcode: 返回一个int数 Object 默认(内部地址转化为一个数字)
两者关系
-
如果equals(obj)返回true, 那么 hashCode一定返回一样的值
-
如果equals(obj)返回false, 那么 hashCode 不一定返回一样的值
-
如果hashCode 返回相同的数,那么 equals 不一定返回true
-
如果hashCode返回不一样的数,那么equals 一定返回false
集合中使用
放入流程
放入散列时候,先比较hashCode, 然后判断equals
HashSet 放入元素
public boolean add(E e) { return map.put(e, PRESENT)==null; }
HashMap 放入元素
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
测试一: 覆盖 equals 不覆盖 hashCode
public class HashCodeTest { public static void main(String[] args) { Collection set = new HashSet(); Point p1 = new Point(1, 1); Point p2 = new Point(1, 1); System.out.println(p1.equals(p2)); set.add(p1); //(1) set.add(p2); //(2) set.add(p1); //(3) Iterator iterator = set.iterator(); while (iterator.hasNext()) { Object object = iterator.next(); System.out.println(object); } } } class Point { private int x; private int y; public Point(int x, int y) { super(); this.x = x; this.y = y; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Point other = (Point) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } @Override public String toString() { return "x:" + x + ",y:" + y; } }
输出
true x:1,y:1 x:1,y:1
分析
(1)当执行set.add(p1)时(1),集合为空,直接存入集合;
(2)当执行set.add(p2)时(2),首先判断该对象(p2)的hashCode值所在的存储区域是否有相同的hashCode,因为没有覆盖hashCode方法,所以jdk使用默认Object的hashCode方法,返回内存地址转换后的整数,因为不同对象的地址值不同,所以这里不存在与p2相同hashCode值的对象,因此jdk默认不同hashCode值,equals一定返回false,所以直接存入集合。
(3)当执行set.add(p1)时(3),时,因为p1已经存入集合,同一对象返回的hashCode值是一样的,继续判断equals是否返回true,因为是同一对象所以返回true。此时jdk认为该对象已经存在于集合中,所以舍弃。
测试二:覆盖hashCode方法,但不覆盖equals方法,仍然会导致数据的不唯一性
class Point { private int x; private int y; public Point(int x, int y) { super(); this.x = x; this.y = y; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } @Override public String toString() { return "x:" + x + ",y:" + y; } }
输出
false x:1,y:1 x:1,y:1
分析
测试三 内存泄漏问题
public class HashCodeTest { public static void main(String[] args) { Collection set = new HashSet(); Point p1 = new Point(1, 1); Point p2 = new Point(1, 2); set.add(p1); set.add(p2); p2.setX(10); p2.setY(10); set.remove(p2); Iterator iterator = set.iterator(); while (iterator.hasNext()) { Object object = iterator.next(); System.out.println(object); } } } class Point { private int x; private int y; public Point(int x, int y) { super(); this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Point other = (Point) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } @Override public String toString() { return "x:" + x + ",y:" + y; } }
结果
x:1,y:1
x:10,y:10
分析
假设p1的hashCode为1,p2的hashCode为2,在存储时p1被分配在1号桶中,p2被分配在2号筒中。这时修改了p2中与计算hashCode有关的信息(x和y),当调用remove(Object obj)时,首先会查找该hashCode值得对象是否在集合中。假设修改后的hashCode值为10(仍存在2号桶中),这时查找结果空,jdk认为该对象不在集合中,所以不会进行删除操作。然而用户以为该对象已经被删除,导致该对象长时间不能被释放,造成内存泄露。解决该问题的办法是不要在执行期间修改与hashCode值有关的对象信息,如果非要修改,则必须先从集合中删除,更新信息后再加入集合中。
总结
1.hashCode是为了提高在散列结构存储中查找的效率,在线性表中没有作用。
2.equals和hashCode需要同时覆盖。
3.若两个对象equals返回true,则hashCode有必要也返回相同的int数。
4.若两个对象equals返回false,则hashCode不一定返回不同的int数,但为不相等的对象生成不同hashCode值可以提高哈希表的性能。
5.若两个对象hashCode返回相同int数,则equals不一定返回true。
6.若两个对象hashCode返回不同int数,则equals一定返回false。
7.同一对象在执行期间若已经存储在集合中,则不能修改影响hashCode值的相关信息,否则会导致内存泄露问题。
原文:https://blog.csdn.net/haobaworenle/article/details/53819838