HashMap的key是对象!

标题即结论,java中HashMap的key是对象,需要谨慎操作。

背景

      最近遇到(其实是亲手写出的)一个小bug,用HashMap<Integer, Boolean>存储了一些东西,但是在get数据的时候,key用的是long类型,导致结果读不出来。

实验

      用一个小实验说明一下

public class Test {
    public static void main(String[] args) throws ParseException {
        HashMap<Integer, Boolean> map = new HashMap<>();
        map.put(188, true);
        System.out.println(map.get(188));
        System.out.println(map.get(188l));
    }

}

      输出结果为

true
null

分析

      表面上看,是存储的时候存的是int类型,读的时候用long类型就读不到。

      但这个说法不精准,HashMap里存的是对象,而不是基础类型。所以肯定是存的Integer类型的对象,用Long类型的对象去查找是找不到的。

      这里其实涉及到了java基础类型的自动装箱,那么自动装箱是怎么做的呢?

扩展

      上面的代码,188 和188l 值其实是一样的,为啥找不到呢?是因为java有对基础类型的自动装箱,188最后成为了Integer类型的对象,188l最后成为了Long类型的对象。具体是怎么做到的呢?把代码反编译一下就可以看到了

      反编译命令:   javap - c Test     (这个Test指定的是class文件,-v显示的信息更多,但是对于这个分析来说-c足够了)

public static void main(java.lang.String[]) throws java.text.ParseException;
    Code:
       0: new           #2                  // class java/util/HashMap
       3: dup
       4: invokespecial #3                  // Method java/util/HashMap."<init>":()V
       7: astore_1
       8: aload_1
       9: sipush        188                //把188 压入程序栈
      12: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 调用Integer.valueOf来把188自动装箱变成Integer对象
      15: iconst_1
      16: invokestatic  #5                  // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
      19: invokevirtual #6                  // Method java/util/HashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
      22: pop
      23: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      26: aload_1
      27: sipush        188    // 这是第一个get(188),下面转成Integer对象,然后调用get方法
      30: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;  
      33: invokevirtual #8                  // Method java/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
      36: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      39: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      42: aload_1
      43: ldc2_w        #10                 // long 188l  这是第get(188l),下面转成Long对象,然后调用get方法
      46: invokestatic  #12                 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
      49: invokevirtual #8                  // Method java/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
      52: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      55: return

      通过上面的代码我们可以看到数字都被Integer.valueOf 和 Long.valueOf 转成了对象,这就是自动装箱的逻辑。

      至于,为什么Integer的188和Long的188不能被判断为相等,但是两个Integer的188可以被判断为相等,我们可以去看HashMap的get方法实现,传进来的对象首先计算hash,去找对应的hash是否存在;在hash存在的情况下再去判断对应的key是否真正存在,这里判断是否存在先用的是对象的==,然后用的是equals方法,二者满足其一即可。

      Integer(188)调用equals(Long(188))肯定是返回false的。

总结

    一个小问题能扯这么多,给自己点赞👏

 

 

      

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值