【effective Java读书笔记】对于所有对象都通用的方法(二)
2017-08-09 10:16
323 查看
一、覆盖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; } @Override public 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.Test public void test() { Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>(); m.put(new PhoneNumber(707, 867, 5309), "Jenny"); //取出结果为null System.out.println(m.get(new PhoneNumber(707, 867, 5309))); }执行代码二:使用ArrayList执行结果输出为true
@org.junit.Test public void test1() { List<PhoneNumber> m = new ArrayList<PhoneNumber>(); m.add(new PhoneNumber(707, 867, 5309)); //结果为true System.out.println(m.contains(new PhoneNumber(707, 867, 5309))); }执行代码一是没有put进去这个对象么?不。debug发现已经加入了HashMap;
然后看看问题出在哪?猜想可知出在HashMap的get方法,看看源码:
public V get(Object
key) {
Node<K,V>
e;
return (e = getNode(hash(key),key)) ==
null ? null : e.value;
}
hash(key)源码如下:
static
final 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
public
int 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.Test public 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]]
相关文章推荐
- 对于所有对象都通用的方法(equals、hashCode、clone)
- effective java 读书笔记---第三章对于所有对象都通用的方法
- Effective Java - 对于所有对象都通用的方法 - 覆盖 equals 时请遵守通用约定
- Effective Java 3:对于所有对象都通用的方法
- Effective Java读书笔记(第3章-对于所有对象都通用的方法)
- Effactive Java -- 对于所有对象都通用的方法
- 【effective Java读书笔记】对于所有对象都通用的方法(一)
- Effective Java:对于所有对象都通用的方法
- 《Effective Java》 第二讲:对于所有对象都通用的方法
- 对于所有对象都通用的方法(二)
- 10. 【对于所有对象都通用的方法】尽量覆盖toString方法
- 9. 【对于所有对象都通用的方法】重写equals方法时一定也要重写hashCode方法
- Effective Java-第三章 对于所有对象都通用的方法
- Effective Java学习笔记-对于所有对象都通用的方法
- effective java读书笔记——对于所有对象都通用的方法
- 《Effective Java》——学习笔记(对于所有对象都通用的方法&类和接口)
- Effective Java Note(对于所有对象都通用的方法)
- 对于所有对象都用的通用方法
- Effective Java - 对于所有对象都通用的方法 - 覆盖 equals 时请总要覆盖 hashCode
- Effective Java:对于所有对象都通用的方法