【effective Java读书笔记】对于所有对象都通用的方法(二)
来源:互联网 发布:淘宝邮票真假怎么鉴定 编辑:程序博客网 时间:2024/06/01 09:38
一、覆盖equals时总要覆盖hashCode
equals上节讲完之后,似乎比较两个对象的时候自己覆盖equals就非常好用了。然而,如果仅仅只是覆盖equals在HashMap中使用的时候会出现意料之外的结果。
如下代码:只覆盖了equals,没有覆盖hashCode方法。
public class PhoneNumber {private final short areaCode;private final short prefix;private final short lineNumber;public PhoneNumber(short areaCode, short prefix, short lineNumber) {super();this.areaCode = areaCode;this.prefix = prefix;this.lineNumber = lineNumber;}public PhoneNumber(int i, int j, int k) {this.areaCode = (short)i;this.prefix = (short)j;this.lineNumber = (short)k;}@Overridepublic boolean equals(Object obj) {if (obj == this) {return true;}if (!(obj instanceof PhoneNumber)) {return false;}PhoneNumber pn = (PhoneNumber) obj;return pn.areaCode==areaCode&&pn.prefix==prefix&&pn.lineNumber==lineNumber;}}执行代码一:使用HashMap执行结果输出为null
@org.junit.Testpublic void test() {Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();m.put(new PhoneNumber(707, 867, 5309), "Jenny");//取出结果为nullSystem.out.println(m.get(new PhoneNumber(707, 867, 5309)));}执行代码二:使用ArrayList执行结果输出为true
@org.junit.Testpublic void test1() {List<PhoneNumber> m = new ArrayList<PhoneNumber>();m.add(new PhoneNumber(707, 867, 5309));//结果为trueSystem.out.println(m.contains(new PhoneNumber(707, 867, 5309)));}执行代码一是没有put进去这个对象么?不。debug发现已经加入了HashMap;
然后看看问题出在哪?猜想可知出在HashMap的get方法,看看源码:
public V get(Objectkey) {
Node<K,V>e;
return (e = getNode(hash(key),key)) ==null ? null : e.value;
}
hash(key)源码如下:
staticfinal int hash(Object key) {
int h;
return (key ==null) ? 0 : (h =key.hashCode()) ^ (h >>> 16);
}
调用了HashMap的getNode方法;
//hash等于传入对象的hash值,key是指传入对象final Node<K,V> getNode(int hash, Object key) {//Node数组tab,Node值first Node<K,V>[] tab; Node<K,V> first, e; int n; K k; //table中数据给tab一份,first等于tab的最后一个值和hash值都不为空才等于tab的最后一个值; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //first.hash如果等于传入对象的hash值,才继续,如果再满足对象引用相等或者对象值相等返回first对象 if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; //如果first.next不为空 if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { //e.hash如果等于传入对象的hash值,才继续,如果再满足对象引用相等或者对象值相等返回e对象 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }上述源码写了详细的注释,判断这个对象new PhoneNumber(707, 867, 5309)是否在m中,断点发现第一个条件不满足:
(tab =table) != null && (n =tab.length) > 0 &&
(first =tab[(n - 1) &hash]) !=null
然后再看上面加入的时候对象的hashCode是1766751238,而取出的时候hashCode是1174361318,导致两个相等对象不同的hashCode码。所以返回null。根据getNode的源码,发现HashMap自身还是有优化的,先去判断HashCode是否相等,如果不相等就不继续比较了,也就不存在equals方法比较了。棒棒哒!解决这个问题非常好解决,只需要将对象相等的对象hashCode相等即可。在PhoneNumber中加入下面代码:
@Override
publicint hashCode() {
return 42;
}
返回一个固定值,那么是否很完美?不,这种算法那么就相当于将所有数据都塞入一个散列桶里了。将Hash的优化给干没了,性能也就相当于list的算法。如何写出一个好的散列码呢?需遵守“为不相等的对象产生不相等的散列码”。
当然,此处我一般都用现代ide提供的自动生成HashCode方案。大概看一眼,String如何生成HashCode的方法,
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
二、始终要覆盖toString
至于问为什么?看个例子,
@org.junit.Testpublic void test2() {List<PhoneNumber> m = new ArrayList<PhoneNumber>();m.add(new PhoneNumber(707, 867, 5309));System.out.println(m.toString());}如果没覆盖得到的结果:
[hashTest.PhoneNumber@2a]
如果覆盖得到的结果:[PhoneNumber [areaCode=707, prefix=867, lineNumber=5309]]
- 《Effective Java》读书笔记(二)之对于所有对象都通用的方法
- 【effective Java读书笔记】对于所有对象都通用的方法(二)
- Effective Java 读书笔记(二):对于所有对象都通用的方法
- Effective Java读书笔记(第3章-对于所有对象都通用的方法)
- 【读书笔记】《Effective Java》(2)--对于所有对象都通用的方法
- 【effective Java读书笔记】对于所有对象都通用的方法(一)
- 【effective Java读书笔记】对于所有对象都通用的方法(三)
- Effective Java(二) 对于所有对象都通用的方法
- Effective Java学习笔记(二)对于所有对象都通用的方法
- Effective Java读书笔记(3对于所有对象都通用的方法)
- effective java-读书笔记-第三章 对于所有对象都通用的方法
- Effective Java读书笔记——第三章 对于所有对象都通用的方法
- effective java 读书笔记---第三章对于所有对象都通用的方法
- Effective Java:对于所有的对象都通用的方法
- effective java(对于所有对象都通用的方法)
- Effective Java:对于所有对象都通用的方法
- Effective Java Note(对于所有对象都通用的方法)
- Effective Java:对于所有对象都通用的方法
- 大数据分析你不能不懂的6个核心技术
- HDU 3068最长回文
- 蓝牙音响升压芯片,12V输出,FP6293
- Java基础之类之间的关系
- 康托 逆康托
- 【effective Java读书笔记】对于所有对象都通用的方法(二)
- H5项目常见问题及注意事项
- 滑动解锁效果
- HDU 3294Girls' research
- Open-falcon Agent模块rpc通信过程
- zigbee 关联表AssociatedDevList
- 从零开始搭建环境编写操作系统 AT&T GCC (十)多任务
- 10、maven学习总结
- leetcode--Number of Islands