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 变量是 null 呢,感觉不是 byte 自动转型为 int,再自动装箱为 Integer 吗
1
IMCA1024 2020-03-30 13:52:14 +08:00
.....Integer 做 key 啊。
为什么推荐用 String 做 key 呢? 原理我不太会说,但我觉得问题在 你这个 byte b=1 的 hash 值 并不能拿到 key 为 Integer=1 的。。 希望楼下的能给我说明一下 哈哈哈 |
2
mm163 2020-03-30 13:54:30 +08:00
HashMap<Integer, String> map= new HashMap<Integer, String>();
|
3
EPr2hh6LADQWqRVH 2020-03-30 13:57:30 +08:00 via Android 1
要射自己脚的话直接 c++就完了嘛
|
4
earther01 2020-03-30 14:01:11 +08:00
看了下 get 前强转一下就行了,不转的话应该是认为传进去的是 object 的地址,会先寻址找到地址对应的 object
|
5
airfling 2020-03-30 14:03:21 +08:00
我刚刚测试了下你没加强转的话是取 byte 的 hash 值取值的,加了强转的话就是一样的值了
下面是 map 中的 get ~~~ public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } ~~~ map.get((int)b) 这样获取的值就是一样的 |
6
h3nng 2020-03-30 14:15:26 +08:00
补充下楼上说的,hash 值其实是一样的,都是 1,但是 == 或 equals() 为 false
|
7
amiwrong123 OP @airfling
好吧,大概懂了。我主要之前以为函数签名是泛型类型呢 public V get(K key) { |
8
p2pCoder 2020-03-30 14:33:29 +08:00
只会触发装箱,没有触发转型
|
9
lux182 2020-03-30 14:59:32 +08:00
个人猜测:byte 封装为 Byte 。Byte _b = 1 与 Integer _a = 1 的 hash 、equals 应该有不同
|
10
ChenFanlin 2020-03-30 15:23:28 +08:00
|
11
yeqizhang 2020-03-30 16:29:12 +08:00 via Android
@mm163 不是应该这么写?:HashMap map= new HashMap<Integer, String>();
|
12
guyeu 2020-03-30 16:31:16 +08:00
因为 HashMap.get(Object)接受任意类型的参数,当传入基本数据类型时,会触发自动装箱。
如果你声明一个这样的方法,用来替换 HashMap.get ,就会先触发类型转换,然后触发自动装箱; ```java static <T> T get(HashMap<?, T> map, int key) { return map.get(key); } ``` java 貌似不支持同一个位置既自动类型转换又自动装箱。。 |
13
elevation 2020-03-30 16:35:15 +08:00
如果想彻底了解,需要学习 HashMap 源码中 put 运行机制;还有你需要写明白,自己的开发环境
|
15
amiwrong123 OP @guyeu
java 不支持同一个位置既自动类型转换又自动装箱么,回头我试下 对,自己再写个方法可以哈。话说,你那个泛型方法 应该这样吧: ```java static <K,V> V get(HashMap<K, V> map, K key) { return map.get(key); } ``` |
18
guyeu 2020-03-30 22:17:59 +08:00
@amiwrong123 #15 你这样写没办法出发自动类型转换(你这个只不过是把 HashMap 自带的 get 方法换了个写法)
|
19
dreamist 2020-03-30 23:17:36 +08:00
这个代码,在 Kotlin 里面是会编译报错的,所以,Kotlin 欢迎你~~ hhh
|
20
dreamist 2020-03-30 23:20:40 +08:00
这个问题,究其原因,还是 HashMap 的锅,HashMap 的 get 方法参数是没有泛型约束的:
public V get(Object key) { } 所以在 get 的时候,传入的类型,是允许和 HashMap 定义时的 key 类型是不一致的,这就导致了这样的问题无法在编译期间暴露出来。 |
21
1194129822 2020-03-30 23:45:42 +08:00 via Android 1
楼主对 hashmap,基本类型及其包装类,自动装箱及隐私类型转换,类型提升不是很熟悉啊。首先 Java 泛型并不支持基本类型,java 默认的整型是 int,浮点数是 double,所以没有特别标注 1,2 这些字面量表示就是 int,当在泛型方法中使用时,会自动装箱为 Integer,如果强行指定(byte)1 则会包装为 Byte,hashmap 判定两个元素是否相等是 equals,而 hashcode 相同只是处在相同的 bin 中。所以当然为 null 啦,而 Integer(1)与 Byte(1)当然就不相同啦,这里还有个坑,因为 Byte,Integer 缓存了 1byte 的值,所以你 put/get(1)多少次只是 1 个包装对象,而 get(128)就会每次生成一个新对象,虽然包装类都重写了 equals,但还是会稍微影响点性能,而 java 默认的隐式类型装是针对基本类型的,换是 int 可以接受 byte,注意 Byte 不能转换为 Integer 。而类型提升也是针对基本类型的,算数位移等运算符只能是基本类型,并且默认提升到***int***,所以 byte(1)+byte(1)=int(2). 所以你只要 get(byte(1)+0)就相当于 get(1)了,推荐看一下 java 规范
|
22
xiaowangge 2020-03-31 00:42:50 +08:00 1
1 、debug 大法好:
在 IDEA 中,在 `String bb = map.get(b);` 这一行打断点,然后 debug 运行,force step into public static Byte valueOf(byte b) { final int offset = 128; return ByteCache.cache[(int)b + offset]; } 2 、javap 大法好: 35: invokestatic #9 // Method java/lang/Byte.valueOf:(B)Ljava/lang/Byte; |
23
Aresxue 2020-03-31 15:21:18 +08:00
很有趣的问题, 看了下源码 byte b 在处理时被装箱成了 Byte,有趣的是 new Byte(1)和 new Integer(1)的 hashCode 是一样的, 这很容易让人困惑, 但在 HashMap 569 行(k = first.key) == key || (key != null && key.equals(k))) 中对 key 除了 hashCode 的判断还有对类型的判断(见 Integer equals 方法 974 行)。同理你使用 map.get(new Integer(1)) 就可以取出对应的值来, 哪怕不是同一个对象也依旧可以取出你想要的值, 因为 new Integer(1) equals new Integer(1)
|
24
SoloCompany 2020-03-31 19:47:42 +08:00
关键在于 Map.get 的签名是 get(Object key) 而并不是 get(K key)
虽然 put 的签名是 put(K key, V value) 然而 get 和 put 并不对等 |