前言
这个星期在写代码的时候发现一个问题,这个问题以前我都没有注意过,就是关于Integer类型或int类型的值的比较。
Integer大小比较问题
描述一
下面演示,我出现的问题:
public class DemoApplication {
public static void main(String[] args) {
Integer code = 530000;
City city = City.init();
Integer c_code = city.getCode();
if (c_code == code) {
System.out.println("结果:true");
} else {
System.out.println("结果:false");
}
}
static class City {
private Integer id;
private Integer code;
private String name;
private Integer pcode;
private Integer level;
public static City init() {
City city = new City();
city.setId(1);
city.setCode(530000);
city.setName("云南省");
city.setPcode(0);
city.setLevel(1);
return city;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPcode() {
return pcode;
}
public void setPcode(Integer pcode) {
this.pcode = pcode;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
}
}
输出 结果:false
原因是:两个Integer类型的值,它们的地址值是不相等的。用”==”判断的是它们的地址值是否相等。所以结果输出false。
可以使用System.identityHashCode()方法查看对应变量的内存地址
System.out.println(System.identityHashCode(code));
System.out.println(System.identityHashCode(c_code));
最后,用Integer里面的equals方法进行比较值是否相等。
if (c_code.equals(code)) {
System.out.println("结果:true");
} else {
System.out.println("结果:false");
} `输出 结果:true`
结论:两个Integer类型对象,如果判断值相等要用equals方法,而不能用”==”。equals方法判断的是值是否相等,==判断的是地址值是否相等。
接下来,我们看一下Integer的equals方法的源码:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
由上面的Integer的equals方法的源码可知: 首先判断比较的这个值obj是否是Integer类型,如果是则把obj强制转换为Integer类型,然后再次拆箱(Integer转为int)为int类型,再比较它们的值是否相等。
到此,忽然想到一个问题,请往下看。
我把code这个变量类型改为int类型,原来是Integer类型,请问输出是多少?
int code = 530000;
City city = City.init();
Integer c_code = city.getCode();
if (c_code == code) {
System.out.println("结果:true");
} else {
System.out.println("结果:false");
}
if (c_code.equals(code)) {
System.out.println("结果:true");
} else {
System.out.println("结果:false");
}
两个if输出结果都是,结果:true
一个是Integer类型的,一个是int类型的值的比较,用==比较为什么输出的是true?原因是,上面c_code是Integer类型,因为java存在自动装拆箱的机制,c_code自动拆箱为int类型,相当于if(c_code.intValue() == code),所以它们就可以用==比较值是否相等了。
结论:一个Integer类型的值和int类型的值比较,使用”==”或者Integer的equals方法都可以比较出它们是否相等。
总结:
如果两个Integer类型对象,判断值相等要用equals方法,而不能用”==”。equals方法判断的是值是否相等,==判断的是地址值是否相等。
如果一个Integer类型的值和int类型的值比较,使用”==”或者Integer的equals方法都可以比较出它们是否相等。
如果两个int类型的值做比较,则使用”==”。
描述二
下面演示问题:
Integer a = 10;
Integer b = 10;
System.out.println("a==b : " + String.valueOf(a==b));
System.out.println("a.equals(b) : " + String.valueOf(a.equals(b)));
输出结果:
a==b : true
a.equals(b) : true
变换一下a,b的值:
Integer a = 1000;
Integer b = 1000;
System.out.println("a==b : " + String.valueOf(a==b));
System.out.println("a.equals(b) : " + String.valueOf(a.equals(b)));
输出结果:
a==b : false
a.equals(b) : true
这是什么原因呢? 我们在使用类似 Integer a = 1000 时,Integer这个类是调用的public static Integer valueOf(int i)这个方法完成自动装箱,把int类型自动转换为Integer类型。
自动装箱涉及到的方法是Integer.valueOf(),看看其源代码如下:
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
上面源代码的注释里就直接说明了-128到127之间的值都是直接从缓存中取出的
。实现:如果int型参数i在IntegerCache.low和IntegerCache.high范围内,则直接由IntegerCache返回;否则new一个新的对象返回。IntegerCache.low是-128,IntegerCache.high是127。
IntegerCache的源码:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
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) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 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;
cache = new Integer[(high - low) + 1];
int j = low;
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;
}
private IntegerCache() {}
}
在其static块中就一次性生成了-128到127直接的Integer类型变量存储在cache[]中,对于-128到127之间的int类型,返回的都是同一个Integer类型对象。
Integer.class在装载(Java虚拟机启动)时,其内部类型IntegerCache的static块即开始执行,实例化并暂存数值在-128到127之间的Integer类型对象。当自动装箱int型值在-128到127之间时,即直接返回IntegerCache中暂存的Integer类型对象。
为什么Java这么设计?应该是出于效率,性能的考虑,因为自动装箱经常遇到,尤其是小数值的自动装箱;而如果每次自动装箱都触发new,在堆中分配内存,就显得太慢了;所以不如预先将那些常用的值提前生成好,自动装箱时直接拿出来返回。哪些值是常用的?就是-128到127了。
不仅int,Java中的另外7中基本类型都可以自动装箱和自动拆箱,其中也有用到缓存。见下表:
基本类型 | 装箱类型 | 取值范围 | 是否缓存 | 缓存范围 |
---|---|---|---|---|
byte | Byte | -128 ~ 127 | 是 | -128 ~ 127 |
short | Short | -2^15 ~ (2^15 - 1) | 是 | -128 ~ 127 |
int | Integer | -2^31 ~ (2^31 - 1) | 是 | -128 ~ 127 |
long | Long | -2^63 ~ (2^63 - 1) | 是 | -128 ~ 127 |
float | Float | – | 否 | – |
double | Double | – | 否 | – |
boolean | Boolean | true, false | 是 | true, false |
char | Character | \u0000 ~ \uffff | 是 | \u0000 ~ \u007f |