V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX  ›  Java

对 HashMap<Integer, String>调用 get(byte 变量) 为何取不到值?

  •  
  •   amiwrong123 · 2020-03-30 13:46:11 +08:00 · 4597 次点击
    这是一个创建于 1700 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 吗

    24 条回复    2020-03-31 19:47:42 +08:00
    IMCA1024
        1
    IMCA1024  
       2020-03-30 13:52:14 +08:00
    .....Integer 做 key 啊。
    为什么推荐用 String 做 key 呢?

    原理我不太会说,但我觉得问题在 你这个 byte b=1 的 hash 值 并不能拿到 key 为 Integer=1 的。。
    希望楼下的能给我说明一下 哈哈哈
    mm163
        2
    mm163  
       2020-03-30 13:54:30 +08:00
    HashMap<Integer, String> map= new HashMap<Integer, String>();
    EPr2hh6LADQWqRVH
        3
    EPr2hh6LADQWqRVH  
       2020-03-30 13:57:30 +08:00 via Android   ❤️ 1
    要射自己脚的话直接 c++就完了嘛
    earther01
        4
    earther01  
       2020-03-30 14:01:11 +08:00
    看了下 get 前强转一下就行了,不转的话应该是认为传进去的是 object 的地址,会先寻址找到地址对应的 object
    airfling
        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) 这样获取的值就是一样的
    h3nng
        6
    h3nng  
       2020-03-30 14:15:26 +08:00
    补充下楼上说的,hash 值其实是一样的,都是 1,但是 == 或 equals() 为 false
    amiwrong123
        7
    amiwrong123  
    OP
       2020-03-30 14:29:45 +08:00
    @airfling
    好吧,大概懂了。我主要之前以为函数签名是泛型类型呢 public V get(K key) {
    p2pCoder
        8
    p2pCoder  
       2020-03-30 14:33:29 +08:00
    只会触发装箱,没有触发转型
    lux182
        9
    lux182  
       2020-03-30 14:59:32 +08:00
    个人猜测:byte 封装为 Byte 。Byte _b = 1 与 Integer _a = 1 的 hash 、equals 应该有不同
    ChenFanlin
        10
    ChenFanlin  
       2020-03-30 15:23:28 +08:00
    yeqizhang
        11
    yeqizhang  
       2020-03-30 16:29:12 +08:00 via Android
    @mm163 不是应该这么写?:HashMap map= new HashMap<Integer, String>();
    guyeu
        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 貌似不支持同一个位置既自动类型转换又自动装箱。。
    elevation
        13
    elevation  
       2020-03-30 16:35:15 +08:00
    如果想彻底了解,需要学习 HashMap 源码中 put 运行机制;还有你需要写明白,自己的开发环境
    yeqizhang
        14
    yeqizhang  
       2020-03-30 16:36:30 +08:00 via Android
    @mm163 我搞错了,楼主的是没问题的。菱形泛型
    amiwrong123
        15
    amiwrong123  
    OP
       2020-03-30 16:55:34 +08:00
    @guyeu
    java 不支持同一个位置既自动类型转换又自动装箱么,回头我试下

    对,自己再写个方法可以哈。话说,你那个泛型方法 应该这样吧:

    ```java
    static <K,V> V get(HashMap<K, V> map, K key) {
    return map.get(key);
    }
    ```
    yjxjn
        16
    yjxjn  
       2020-03-30 18:17:59 +08:00
    @mm163 楼主这个没写错呀,后面的菱形里面不声明也可以、
    cco
        17
    cco  
       2020-03-30 18:55:36 +08:00
    @yeqizhang HashMap<String, Object> map= new HashMap<>();
    guyeu
        18
    guyeu  
       2020-03-30 22:17:59 +08:00
    @amiwrong123 #15 你这样写没办法出发自动类型转换(你这个只不过是把 HashMap 自带的 get 方法换了个写法)
    dreamist
        19
    dreamist  
       2020-03-30 23:17:36 +08:00
    这个代码,在 Kotlin 里面是会编译报错的,所以,Kotlin 欢迎你~~ hhh
    dreamist
        20
    dreamist  
       2020-03-30 23:20:40 +08:00
    这个问题,究其原因,还是 HashMap 的锅,HashMap 的 get 方法参数是没有泛型约束的:
    public V get(Object key) {
    }

    所以在 get 的时候,传入的类型,是允许和 HashMap 定义时的 key 类型是不一致的,这就导致了这样的问题无法在编译期间暴露出来。
    1194129822
        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 规范
    xiaowangge
        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;
    Aresxue
        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)
    SoloCompany
        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 并不对等
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1100 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 18:27 · PVG 02:27 · LAX 10:27 · JFK 13:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.