• Integer类型比较大小【详解】


    面试中经常被问的一个问题就是Integer类型的对象的比较问题,今天我们就来一探究竟

    有如下代码:问输出是什么?

    public static void main(String[] args) {
            Integer a = 128;
            Integer b = 128;
    
            Integer c = 1;
            Integer d = 1;
    
            System.out.println(a==b);
            System.out.println(a.equals(b));
            System.out.println(c==d);
            System.out.println(c.equals(d))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    //输出:
    false
    true
    true
    true
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1、equals()

    首先我们看比较容易理解的a.equals(b)c.equals(d),我们看下源码,主要涉及代码如下:

    private final int value;
    public boolean equals(Object obj) {
           if (obj instanceof Integer) {
               return value == ((Integer)obj).intValue();
           }
           return false;
       }
       public int intValue() {
           return value;
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到Integer中重写了equals方法,其中就是对于int类型值的比较,所以自然返回true

     System.out.println(a.equals(b));//等价于128==128
     System.out.println(c.equals(d));//等价于1==1
    
    • 1
    • 2

    2、==

    下面的==比较就比较麻烦了,其实也不麻烦,只是涉及到自动拆箱和Integer对象缓存,
    我们先来看一下字节码:javap -v 反编译一下,找到main方法对用的字节码如下:

    public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=5, args_size=1
             0: sipush        128
             3: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
             6: astore_1
             7: sipush        128
            10: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
            13: astore_2
            14: iconst_1
            15: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
            18: astore_3
            19: iconst_1
            20: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
            23: astore        4
            25: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            28: aload_1
            29: aload_2
            30: if_acmpne     37
            33: iconst_1
            34: goto          38
            37: iconst_0
            38: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
            41: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            44: aload_1
            45: aload_2
            46: invokevirtual #5                  // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
            49: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
            52: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            55: aload_3
            56: aload         4
            58: if_acmpne     65
            61: iconst_1
            62: goto          66
            65: iconst_0
            66: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
            69: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            72: aload_3
            73: aload         4
            75: invokevirtual #5                  // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
            78: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
            81: return
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    通过字节码可以看到,从0-23是在初始化了4个变量,同时存储之前自动调用了java/lang/Integer.valueOf:(I)Ljava/lang/Integer;进行装箱,25-38对应代码System.out.println(a==b);

     25: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     28: aload_1							//加载128
     29: aload_2							//加载128
     30: if_acmpne     37					//比较两个Integer引用是否是!=,成立执行33行,将1写入操作数栈,不成立跳转到37,将0写入操作数栈
     33: iconst_1
     34: goto          38
     37: iconst_0
     38: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V//打印30行执行后的结果0或1对应false或true
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    问题就出现这里,通过输出的结果我们可以看到a和b引用的是不同的Integer,这个可以理解,因为我们就是定义了两个Integer;但是c和d引用的却是同一个Integer,那为什么会这样呢?

    public static void main(String[] args) {
            Integer a = 128;
            Integer b = 128;
            Integer c = 1;
            Integer d = 1;
            System.out.println(a==b);
            System.out.println(a.equals(b));
            System.out.println(c==d);
            System.out.println(c.equals(d))
    }
    //输出:
    false
    true
    true
    true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    玄机应该是在创建对象的时候,编译器悄悄的干了点什么,干了什么呢?其实就是自动装箱,我们找到自动装箱执行的方法Integer.valueOf的源代码如下:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到,当i[IntegerCache.low, IntegerCache.high]中时,没有new新的Integer对象,所以我们在找到IntegerCache一探究竟,源代码如下:

    /**
      * 缓存以支持自动装箱的对象标识语义
      * JLS 要求的 -128 和 127(含)。
      *
      * 缓存在第一次使用时初始化。 缓存大小
      * 可以由 {@code -XX:AutoBoxCacheMax=} 选项控制。
      * 在 VM 初始化期间,java.lang.Integer.IntegerCache.high 属性
      * 可以在私有系统属性中设置和保存
      * sun.misc.VM 类。
      */
    private static class IntegerCache {
            static final int low = -128;//缓存的最小值
            static final int high;
            static final Integer cache[];
    
            static {
                // high value may be configured by property
                int h = 127;
                String integerCacheHighPropValue =
                    sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");//可以自己配置缓存的最大值
                if (integerCacheHighPropValue != null) {//如果自己配置了就用自己配置的,同时下面代码保证了,自己配置的值要大于127才生效
                    try {
                        int i = parseInt(integerCacheHighPropValue);
                        i = Math.max(i, 127);//大于127才生效
                        // Maximum array size is Integer.MAX_VALUE
                        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                    } catch( NumberFormatException nfe) {
                        // If the property cannot be parsed into an int, ignore it.
                    }
                }
                high = h;//没有自己配置就用默认128
    
    			//创建缓存数组
                cache = new Integer[(high - low) + 1];
                int j = low;
                //缓存数组中全部填充值Integer对象
                for(int k = 0; k < cache.length; k++)
                    cache[k] = new Integer(j++);
    
                // range [-128, 127] must be interned (JLS7 5.1.7)
                assert IntegerCache.high >= 127;//最大值必须大于127,否则报错。防止自己配置错误
            }
    
            private IntegerCache() {}
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    代码中都加了注解,应该很好理解。
    看到这里应该就很明白了:得出结论
    (1)System.out.println(a==b);中,因为128>127所以,在自动装箱执行valueOf方法时时并没有进入if语句,而是创建的新的Integer对象,所以显示a、b是两个对象的引用,所以输出false。
    (2)同理 System.out.println(c==d);中,1<127,所以进入if语句块,c、d对象都是IntegerCache.cache[1]引用的对象,所以自然是相等的!输出true。

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public static void main(String[] args) {
            Integer a = 128;
            Integer b = 128;
            Integer c = 1;
            Integer d = 1;
            System.out.println(a==b);
            System.out.println(a.equals(b));
            System.out.println(c==d);
            System.out.println(c.equals(d))
    }
    //输出:
    false
    true
    true
    true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    到了这里其实还没有完:如下代码会输出什么呢?

    public static void main(String[] args) {
            Integer e = new Integer(128);
            Integer f = new Integer(128);
            Integer g = new Integer(1);
            Integer h = new Integer(1);
            
            System.out.println(e==f);
            System.out.println(e.equals(f));
            System.out.println(g==h);
            System.out.println(g.equals(h)); 
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用new创建对象是不会触发自动装箱操作的,所以预测会打印false、true、false、true,执行结果如下,和预料的一样。

    false
    true
    false
    true
    
    • 1
    • 2
    • 3
    • 4

    到这里所有关于Integer对象的比较问题就都说清楚了,但是还会有a与e比较、c与g比较的问题,这些衍生问题就很容易了。

    最后再总结一下:==比较的地址,equals方法得看代码怎么写(一般是值的比较)

    that is all!!!

  • 相关阅读:
    vs2019 release模式调试:此表达式有副作用,将不予计算。
    python字典合并的使用注意
    libcef最新下载地址-在VS2015下编译为MD-动态链接
    竞赛 大数据房价预测分析与可视
    人工智能安全与光明时代
    Mycat2+Mysql+Docker搭建简单的主从复制和读写分离
    Springboot 实践(21)服务熔断机制
    【操作系统】MBR主引导目录结构以及作用
    c#using关键字的作用
    数据可视化
  • 原文地址:https://blog.csdn.net/baidu_40120883/article/details/126555765