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

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

  •  
  •   amiwrong123 · 204 天前 · 2819 次点击
    这是一个创建于 204 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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   204 天前
    .....Integer 做 key 啊。
    为什么推荐用 String 做 key 呢?

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

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

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

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

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

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

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

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

    然而 get 和 put 并不对等
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2897 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 14:00 · PVG 22:00 · LAX 07:00 · JFK 10:00
    ♥ Do have faith in what you're doing.