Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
2017-09-20 14:02
447 查看
覆盖equals时总要覆盖hashcode方法
1、这样干的原因是什么?
如果这个类仅仅是重写了equals方法而没有重写hashCode,那么这个类和基于散列的集合类一起工作时就会出现问题。这样的集合包括HashMap,HashSet以及Hashtable。
比如这种情况:
为什么会出现这种情况呢,下面我们看一看Map的源码就清楚了:
put方法把电话号码对象存放在一个散列桶(hash bucket)中,get方法却在另外一个散列桶里面查找。即使这两个实例正好被放到了同一个散列桶里面,get方法也一定会返回null,因为HashMap做了一项优化,将每个项相关联的散列码存放起来,如果hash码不匹配,则不会去检验对象的等同性,充分利用了逻辑与运算的惰性(&&)。
2、规范
首先明确一个概念,两个对象使用equals返回true,则它们的hashCode也一定相等;如果两个对象的hashCode相等,则它们的equals则不一定相等。
Object规范[JavaSE6]:
在应用程序的一次执行期间,只要对象的equals方法的比较操作所用的信息没有被修改,那么对这个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致
如果两个对象根据equals(Object)方法比较结果相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法不一定要产生不一样的整数结果。但是我们应该知道,给不相等的对象产生不相同的整数结果,可以提高散列表(hashtable)的性能。
3、实现hashCode
如何实现hashCode,当然你可以使hashCode返回一个固定的数值,任何对象的hashCode都是一个固定的数值,这没有问题。但当它与基于散列的集合类一起工作时,这些元素将具有相同的散列码,进而使得所有对象都被映射到统一散列桶中,使得散列表退化为链表。具体重写hashCode的算法不在赘述。
始终要覆盖toString
为什么这么干?
原因在于在有的场景下会打印一条日志,日志的内容就是POJO类的属性字段值,这个时候toString的意义很明显的就体现出来了
1、这样干的原因是什么?
如果这个类仅仅是重写了equals方法而没有重写hashCode,那么这个类和基于散列的集合类一起工作时就会出现问题。这样的集合包括HashMap,HashSet以及Hashtable。
比如这种情况:
public final class PhoneNumber { @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PhoneNumber)) return false; PhoneNumber pn = (PhoneNumber) o; return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode; } }
Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>(); m.put(new PhoneNumber(707, 867, 5309), "Jenny"); System.out.println(m.get(new PhoneNumber(707, 867, 5309))); 输出结果为:null
//
这就出错了,对象相等,但是hashCode不相等,所以取不到值。
为什么会出现这种情况呢,下面我们看一看Map的源码就清楚了:
final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; /** * 检查table是否为空,table为HashMap实例中的一个存放数据的数组 * 检查第一个元素的hash码是否为null */ if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { /* * 效率方面的考虑 * 总是将第一个对象拿出来比较,如果符合要求直接返回 * 避免执行循环准备工作的一系列代码 */ if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; //进入循环之前的一系列准备以及检查工作 if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { //循环内部利用&&运算符的惰性,如果hash码不相同就直接跳过了= = if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
put方法把电话号码对象存放在一个散列桶(hash bucket)中,get方法却在另外一个散列桶里面查找。即使这两个实例正好被放到了同一个散列桶里面,get方法也一定会返回null,因为HashMap做了一项优化,将每个项相关联的散列码存放起来,如果hash码不匹配,则不会去检验对象的等同性,充分利用了逻辑与运算的惰性(&&)。
2、规范
首先明确一个概念,两个对象使用equals返回true,则它们的hashCode也一定相等;如果两个对象的hashCode相等,则它们的equals则不一定相等。
Object规范[JavaSE6]:
在应用程序的一次执行期间,只要对象的equals方法的比较操作所用的信息没有被修改,那么对这个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致
如果两个对象根据equals(Object)方法比较结果相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法不一定要产生不一样的整数结果。但是我们应该知道,给不相等的对象产生不相同的整数结果,可以提高散列表(hashtable)的性能。
3、实现hashCode
如何实现hashCode,当然你可以使hashCode返回一个固定的数值,任何对象的hashCode都是一个固定的数值,这没有问题。但当它与基于散列的集合类一起工作时,这些元素将具有相同的散列码,进而使得所有对象都被映射到统一散列桶中,使得散列表退化为链表。具体重写hashCode的算法不在赘述。
始终要覆盖toString
为什么这么干?
原因在于在有的场景下会打印一条日志,日志的内容就是POJO类的属性字段值,这个时候toString的意义很明显的就体现出来了
相关文章推荐
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- Effective Java(覆盖equals时总要覆盖hashcode方法、始终要覆盖toString)
- java对象通用方法之覆盖equals时请遵守通用约定、覆盖equals时总要覆盖hashCode、始终要覆盖toString、考虑实现Comparable接口
- 覆盖Object类的equals、hashCode和toString方法
- 如何覆盖Object类中的equals(),hashCode(),toString()方法
- Effective Java - 对于所有对象都通用的方法 - 始终要覆盖 toString
- 《问题总结》JAVA什么时候要覆盖toString()、equals()、hashCode()方法??为什么要覆盖呢? 《通俗回答》
- Effective Java - 对于所有对象都通用的方法 - 覆盖 equals 时请总要覆盖 hashCode
- Effective Java 对于所用对象都通用的方法 10.始终要覆盖toString,玩转toString
- effective java读书笔记9:覆盖equals方法时总要覆盖hashcode
- 始终都要覆盖toString()方法