V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
amiwrong123
V2EX  ›  Java

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

  •  
  •   amiwrong123 · Mar 30, 2020 · 5479 views
    This topic created in 2223 days ago, the information mentioned may be changed or developed.
    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 replies    2020-03-31 19:47:42 +08:00
    IMCA1024
        1
    IMCA1024  
       Mar 30, 2020
    .....Integer 做 key 啊。
    为什么推荐用 String 做 key 呢?

    原理我不太会说,但我觉得问题在 你这个 byte b=1 的 hash 值 并不能拿到 key 为 Integer=1 的。。
    希望楼下的能给我说明一下 哈哈哈
    mm163
        2
    mm163  
       Mar 30, 2020
    HashMap<Integer, String> map= new HashMap<Integer, String>();
    EPr2hh6LADQWqRVH
        3
    EPr2hh6LADQWqRVH  
       Mar 30, 2020 via Android   ❤️ 1
    要射自己脚的话直接 c++就完了嘛
    earther01
        4
    earther01  
       Mar 30, 2020
    看了下 get 前强转一下就行了,不转的话应该是认为传进去的是 object 的地址,会先寻址找到地址对应的 object
    airfling
        5
    airfling  
       Mar 30, 2020
    我刚刚测试了下你没加强转的话是取 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  
       Mar 30, 2020
    补充下楼上说的,hash 值其实是一样的,都是 1,但是 == 或 equals() 为 false
    amiwrong123
        7
    amiwrong123  
    OP
       Mar 30, 2020
    @airfling
    好吧,大概懂了。我主要之前以为函数签名是泛型类型呢 public V get(K key) {
    p2pCoder
        8
    p2pCoder  
       Mar 30, 2020
    只会触发装箱,没有触发转型
    lux182
        9
    lux182  
       Mar 30, 2020
    个人猜测:byte 封装为 Byte 。Byte _b = 1 与 Integer _a = 1 的 hash 、equals 应该有不同
    ChenFanlin
        10
    ChenFanlin  
       Mar 30, 2020
    yungo8
        11
    yungo8  
       Mar 30, 2020 via Android
    @mm163 不是应该这么写?:HashMap map= new HashMap<Integer, String>();
    guyeu
        12
    guyeu  
       Mar 30, 2020
    因为 HashMap.get(Object)接受任意类型的参数,当传入基本数据类型时,会触发自动装箱。

    如果你声明一个这样的方法,用来替换 HashMap.get ,就会先触发类型转换,然后触发自动装箱;

    ```java
    static <T> T get(HashMap<?, T> map, int key) {
    return map.get(key);
    }
    ```

    java 貌似不支持同一个位置既自动类型转换又自动装箱。。
    elevation
        13
    elevation  
       Mar 30, 2020
    如果想彻底了解,需要学习 HashMap 源码中 put 运行机制;还有你需要写明白,自己的开发环境
    yungo8
        14
    yungo8  
       Mar 30, 2020 via Android
    @mm163 我搞错了,楼主的是没问题的。菱形泛型
    amiwrong123
        15
    amiwrong123  
    OP
       Mar 30, 2020
    @guyeu
    java 不支持同一个位置既自动类型转换又自动装箱么,回头我试下

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

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

    所以在 get 的时候,传入的类型,是允许和 HashMap 定义时的 key 类型是不一致的,这就导致了这样的问题无法在编译期间暴露出来。
    1194129822
        21
    1194129822  
       Mar 30, 2020 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  
       Mar 31, 2020   ❤️ 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  
       Mar 31, 2020
    很有趣的问题, 看了下源码 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  
       Mar 31, 2020
    关键在于 Map.get 的签名是 get(Object key) 而并不是 get(K key)

    虽然 put 的签名是 put(K key, V value)

    然而 get 和 put 并不对等
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2655 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 141ms · UTC 14:26 · PVG 22:26 · LAX 07:26 · JFK 10:26
    ♥ Do have faith in what you're doing.