对 HashMap调用 get(byte 变量) 为何取不到值?

0. 背景

最近在网上看到了一个问题,觉得挺有意思的,于是就试着分析了一下,并分享给大家。

先看代码:

1
2
3
4
5
6
7
8
9
public class test {
public static void main(String[] args) throws IOException {
HashMap<Integer, String> map= new HashMap<>();
map.put(1,"one");
String aa = map.get(1);
byte b =1;
String bb = map.get(b);
}
}

问题是:对于以上代码中,为何 bb 变量的值会是空的呢?难道不是 byte 自动转型为 int,再自动装箱为 Integer 吗?

问题来自V2EX : https://v2ex.com/t/657545#reply20

那么,我们接下来试着解答一下这个问题。

1. 问题分析

以上的代码很简单,它做了以下几件事件:

  1. 初始化了一个 HashMap 类型的变量 map ;
  2. 向map中添加了一对数据<1, "one"="">;
  3. 初始化一个 byte 类型的变量 b,同时也设置值为1;
  4. 试图使用变量b作为key从map中取出之前存放的KV对数据<1, "one"="">;
  5. 结果获取到的为空了;

单从这个顺序描述来看,好像最终的结果确实是不应该为空的,那么问题到底出在哪里呢?

首先呢,我们知道HashMap中存储数据的原理,它内部是通过Key对象的哈希值作为内部存储的索引值的。

同时,HashMap也像其他的Java集合类一样,只接受引用类型的数据的存储,而不接受原始类型。原始类型作为参数传递进去的时候,会被自动装箱成它们分封装类,比如:int –> Integer、byte –> Byte 等等。

所以在以上的示例代码中,作为key的byte类型或者是int 类型必然也是被自动包装成它们的包装类了。所以以上代码其实是相当于:

1
2
3
4
5
6
7
8
9
10
public class test {
public static void main(String[] args) throws IOException {
HashMap<Integer, String> map= new HashMap<>();
map.put(Integer.valueOf(1),"one");
String aa = map.get(Integer.valueOf(1));
byte b =1;
// HashMap中使用封装类
String bb = map.get(Byte.valueOf(b));
}
}

这样转换后,问题就明了很多了,在代码中调用put的时候是以Integer 类型来当作key存储的,而取数据的时候是以 Byte类型来取的数据,而Java中确定对象是否相等的依据是对象的 equal() 方法。那么Integer(1)Byte(1) 两个连类型都不一样,equals 的结果肯定是false了。

这样分析后,问题就解答了,简而言之,集合类中有自动装箱的存在,所以在涉及到Key的相等判断时,务必要看清除它们的类型。

2. 该如何避免类似的问题呢?

确定了问题后,我们自然要去问自己,在生产环境中,要如何避免这类似的问题呢?而且最好是在编译时就能避免,而不是把问题在运行时才显现出来。

我们来看下HashMap的存取操作相关的方法定义:

1
2
3
4
5
6
7
8
// 存数据
public V put(K key, V value){
}
// 取数据
public V get(Object key) {
}

从存取方法的定义能够看出来,put 方法有泛型的定义来约束,而get方法只有返回值有泛型约束,而输入参数是没有泛型来约束的,而是接收一个Object对象。那么,要是HashMap 的get方法有泛型约束就完美了。

正好!Kotlin 中的HashMap就添加了这个功能,在Kotlin中给常用的集合类都进行了扩展,其中就包括HashMap的get方法的泛型约束问题。

那么,就把以上的代码,换成Kotlin实现的版本,如下:

1
2
3
4
5
6
7
fun test() {
val map: java.util.HashMap<Int, String> = java.util.HashMap()
map[1] = "one"
val aa = map[1]
val b: Byte = 1
val bb = map.get(b)
}

编译下,你就会发现过不了编译~~,所以欢迎入坑Kotlin(没错,这是一篇Kotlin的安利文)。

坚持原创技术分享,您的支持将鼓励我继续创作!