public static void main(String[] args) { Map<Long,Long> map = new HashMap<Long,Long>(); map.put(new Long(1), new Long(1));//这里的key是Long类型 Integer key = new Integer(1); boolean result = map.containsKey(key);//这里的key是Integer类型 System.out.println(result); }
先看一看输出结果,没错,是false,没有任何疑问,这里可以看看HashMap是如何查找value的:HashMap会先比较key的hashCode,然后会直接比较这个key值是否相等(== || equals),这里可以参考我另一篇文章《HashMap源码分析》。下面我把HashMap获取Entry的方法给贴出来,重点看一下里面查找Key的方式。
final Entry<K,V> getEntry(Object key) { int hash = (key == null) ? 0 : hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; }
接着我把Integer和Long的equals方法贴出来,看看这两个类对于equals方法的实现,这样就可以比较清晰的知道为什么在HashMap中containsKey如果里面本来存的key是Integer类型,而调用containsKey传的值是Long类型后得到的结果是false。
public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; }从上面Integer和Long的equals方法可以得出如果比较的两个值都是Integer类型或者Long类型,只要它们的值( intValue/longValue相等),那么equalsr后的结果是true,但是Integer类型和Long类型的两个对象进行比较,即使值(intValue/longValue)相同,equals后是false。这就可以解释上面的一个结论为什么是false了。
public boolean containsKey(Object key) { return getEntry(key) != null; }传进去的参数是 Object类型,这个方法的设定就有点不伦不类,因为HashMap使用了泛型,按照我们一般人的理解,应该会是这样(下面是我改进的方法):
public boolean containsKey(K key) { return getEntry(key) != null; }我所做的改动仅仅是把里面的 Object换成了泛型K。这样的设计有什么好处:再回到上面的那个实例,如果containsKey方法值的是泛型,那么在传值的时候就会约束成Long类型,是不允许传Integer值,这样在编译的时候就会抛错,而不会给整个逻辑留下一个不可预知的隐患,也符合越早发现问题越好的一个设计原则:能把问题留在编译期就不要把问题带到运行期甚至不可发现。这也是为什么要用泛型进行约束,除了代码规范外,另外一个好处就是可以消除因为编码引入的bug。