Java 中 Integer 的 equals 和 == 的区别

近期在 Map<String,Integer> 进行计数比较时,发现 Integer 比较有问题,让我们进行下探究。

如:

Integer a = 281;
Integer b = 281;
assertFalse(a==b);

equals 比较

equals (Object obj) 方法,在 equals (Object obj) 方法中,会先判断参数中的对象 obj 是否是 Integer 同类型的对象,如果是则判断值是否相同,值相同则返回 true,值不同则返回 false,如果 obj 不是 Integer 类的对象,则返回 false。

需要注意的是:当参数是基本类型 int 时,编译器会给 int 自动装箱成 Integer 类,然后再进行比较。

  1. 基本类型 (值类型) 之间无法使用 equals 比较。
  2. equals 参数为值类型,则参数会进行自动装箱为包装类型进行比较。
  3. equals 参数为包装类型,则先比较是否为同类型,非同类型直接返回 false,同类型再比较值。

示例:

//为 false,equals参数默认为int类型,装箱为Integer类型,不同类型直接返回false
assertFalse(new Long(0).equals(0)); 

//为 true,equals参数默认为int类型,装箱为Integer类型,相同类型再比较值返回true
assertTrue(new Integer(500).equals(500)); 

//为 false,equals参数为byte类型,装箱为Byte类型,不同类型直接返回false
assertFalse(new Integer(500).equals((short)500)); 

//为 true,equals参数为long类型,装箱为Long类型,相同类型再比较值返回true
assertTrue(new Long(0).equals(0L)); 

== 比较

  1. 基本类型之间互相比较:以值进行比较
  2. 一边是基本类型,一边是包装类型
    1. 同类型的进行比较,如 Integer 与 int,Long 与 long 进行 == 比较时,会自动拆箱比较值
    2. 不同类型之间进行比较,则会自动拆箱,且会进行自动向上转型再比较值(低级向高级是隐式类型转换如:byte<short<int<long<float<double,高级向低级必须强制类型转换)
  3. 两边都是包装类型则直接比较引用地址,但是要注意 IntegerCache 除外。

IntegerCache 缓存

JAVA 的 Integer 有 IntegerCache 会缓存 - 128~127 之间的对象。

Integer 类的源码中的 IntegerCache 子类如下:

image-20210315174612801

如:Integer x = 100,会调用 Integer 的 valueOf () 方法,这个方法就是返回一个 Integer 对象,但是在返回前,作了一个判断,判断要赋给对象的值是否在 [-128,127] 区间中,且 IntegerCache(是 Integer 类的内部类,里面有一个 Integer 对象数组,用于存放已经存在的且范围在 [-128,127] 中的对象)中是否存在此对象,如果存在,则直接返回引用,否则,创建一个新对象返回。

Integer 类的 valueOf () 方法的源码如下:

image-20210315174709788

当 Integer 在 [-128,127] 范围时:

Integer a = 81;
Integer b = 81;
assertTrue(a==b); //true 因为59位于缓存区间直接从缓存中获取

当 Integer 不在 [-128,127] 范围时:

Integer a = 281;
Integer b = 281;
assertFalse(a==b); //false 因为281超出缓存区间从新创建对象
assertTrue(a.intValue()==b.intValue()); //true 需要 intValue() 转换为 int 进行==比较