也许不是最好的标题 , 请随时编辑它 .
这是我的代码 ..
import java.util.Map;import java.util.HashMap;public class App { public static void main ( String [ ] args ) { final Foo foo = new Foo ( ) ; final Foo bar = new Foo ( ) ; System.out.println ( "foo equals foo: " + foo.equals ( foo ) ) ; System.out.println ( "foo equals bar: " + foo.equals ( bar ) ) ; System.out.println ( "foo hashcode: " + foo.hashCode ( ) ) ; System.out.println ( "bar hashcode: " + bar.hashCode ( ) ) ; final Map<Foo, Integer> foos = new HashMap<Foo, Integer> ( ) ; foos.put ( foo, -99 ) ; System.out.println ( "foos.getfoo: " + foos.get ( foo ) ) ; System.out.println ( "foos.getbar: " + foos.get ( bar ) ) ; } }class Foo { @Override public boolean equals ( Object o ) { return false; } @Override public int hashCode ( ) { return -1; }}
所以在进一步阅读之前你能猜出下面两个语句的输出是什么吗?
System.out.println ( "foos.getfoo: " + foos.get ( foo ) ) ;System.out.println ( "foos.getbar: " + foos.get ( bar ) ) ;
我希望看到:
nullnull
因为即使 hashCodes 匹配 , 对于任何 Foo 实例的 equals 将始终返回 false, 因此使用 Foo 的实例作为 Map 中的键应该根本没有用 .
但输出是:
:~ $javac App.java:~ $java Appfoo equals foo: falsefoo equals bar: falsefoo hashcode: -1bar hashcode: -1foos.getfoo: -99foos.getbar: null
我错过了什么?当我使用一个哈希码为 -1 并且不等于它自己的对象时 , 如何检索 -99, 但是后来使用相同类型的实例获取 null, 该实例也不等于我在 Map 中所拥有的并且还具有哈希码 -1?
最佳答案
因为 HashMap 的 get ( ) 方法被优化以在查看 equals ( ) 之前首先检查对象引用相等性:
查看节点 < K,V> 由 get ( Object key ) 方法调用的 getNode ( int hash,Object key ) 方法:
final Node<K,V> getNode ( int hash, Object key ) { Node<K,V> [ ] tab; Node<K,V> first, e; int n; K k; 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 { if ( e.hash == hash && ( ( k = e.key ) == key || ( key != null && key.equals ( k ) ) ) ) return e; } while ( ( e = e.next ) != null ) ; } } return null;}
这个 :
( k = first.key ) == key
和
( k = e.key ) == key
参考这个优化 .
此外 , 在这里 , 你违反了 equals ( ) 合同的规则
必须是反身的:
class Foo { @Override public boolean equals ( Object o ) { return false; } ...}
for any non-null reference value x, x.equals ( x ) should return true.
从类违反 equals ( ) 契约的那一刻起 , 您无法保证操纵有缺陷类的实例的类将具有预期的行为 .