java基础 - Integer的比较防坑
我们都知道Integer的存储结构,以及和int的自动拆装箱。所以我一般简单处理直接用equals完事,但是还是会踩坑,菜的没边了,,,做个记录。
目录
一、Integer与int
1、int是java中的8大基本数据类型之一。
2、Ingeter是int的包装类,是对象。
3、int的初值为0,Ingeter的初值为null
4、int是基本数据类型,存在常量池。Ingeter的情况比较多,用new的是在堆,对于-128到127之间的数,会进行缓存,Integer i5 = 127时,会将127进行缓存,超出直接new。
java在编译Integer i2 = 128的时候,被翻译成-> Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存。
5、Integer和int会自动拆装箱,所以==直接为值比较,为true
//range [-128, 127]的直接从Integer[] cache缓存中获取
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
//这是Interger的区间和缓存,一般为range [-128, 127]
private static class IntegerCache {
//下区间是-128
static final int low = -128;
//这里的high没给定,但是下面静态代码块给了
static final int high;
static final Integer cache[];
//静态代码块逻辑:
//1、当high属性为空时 low = -128,high=127,cache[127+128+1]
//2、当high属性不为空时 low = -128,high>=127,cache[high+128+1]
//3、遍历填充了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);
//最小127
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
//public static final int MAX_VALUE = 0x7fffffff;
// 0x7FFFFFFF 的二进制表示就是除了首位是 0,其余都是1就是说,这是最大的整型数 int //最大不超过int边界
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() {}
}
private final int value;
public Integer(int value) {
this.value = value;
}
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
测试代码:
@Test
public void intTest() {
//int与integer
int a = 128;
Integer b = 128;
Integer c = new Integer(128);
log.info("a==b|结果{}",a==b);//true 会自动拆箱,值比较
log.info("a==c|结果{}",a==c);//true 会自动拆箱,值比较
log.info("b==c|结果{}",b==c);//false 两个new的对象,地址指针不一样
}
二、Interger与Integer
1、还是需要再次注意,java在编译Integer i2 = 128的时候,被翻译成-> Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存。源码上面有。所以-128到127之间的地址是相同的,都在同一个缓存数组中,超出时会new一个integer,是一个新的对象了,地址指针不一样,==比较为false。
2、new 关键字直接新建一个对象。
3、实现了Comparable, 有comparaTo()方法。
4、Integer继承了Number,它是一个对象,有equals()方法。
private final int value;
//Integer a=new Integer(1);
public Integer(int value) {
this.value = value;
}
//Integer a=new Integer("1");
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
private final int value;
//Integer的compareTo比较最终为int值比较
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
//x与y比较,相等返回0,小于返回-1,大于返回1
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
public boolean equals(Object obj) {
//一定要注意这里,不为Integer类型时,返回false
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
public int intValue() {
return value;
}
测试代码:
@Test
public void integerTest() {
//integer与integer
//range[-128,127]之间比较
Integer a = 127;
Integer b = 127;
Integer c = new Integer(127);
Integer d = new Integer(127);
log.info("127|a==b|结果{}",a==b); //true [-128,127]之间,数组缓存Integer[] cache,地址指针一样
log.info("127|a==c|结果{}",a==c); //flase a为数组缓存Integer[] cache, c直接new对象,地址指针不一样
log.info("127|c==d|结果{}",c==d); //flase new对象,地址指针不一样
log.info("127|a.equals(b)|结果{}",a.equals(b)); //true equals为value值比较
log.info("127|a.equals(c)|结果{}",a.equals(c)); //true equals为value值比较
log.info("127|c.equals(d)|结果{}",c.equals(d)); //true equals为value值比较
log.info("127|a.compareTo(b)|结果{}",a.compareTo(b)); // 0 compareTo也是value值比较
log.info("127|a.compareTo(c)|结果{}",a.compareTo(c)); // 0 compareTo也是value值比较
log.info("127|c.compareTo(d)|结果{}",c.compareTo(d)); // 0 compareTo也是value值比较
Integer a1 = 128;
Integer b1 = 128;
Integer c1 = new Integer(128);
Integer d1 = new Integer(128);
log.info("128|a1==b1|结果{}",a1==b1); //false 128时没有走数组缓存Integer[] cache,直接new Integer(),地址指针不一样
log.info("128|a1==c1|结果{}",a1==c1); //flase a1为数组缓存Integer[] cache, c1直接new对象,地址指针不一样
log.info("128|c1==d1|结果{}",c1==d1); //flase new对象,地址指针不一样
log.info("128|a1.equals(b1)|结果{}",a1.equals(b1)); //true equals为value值比较
log.info("128|a1.equals(c1)|结果{}",a1.equals(c1)); //true equals为value值比较
log.info("128|c1.equals(d1)|结果{}",c1.equals(d1)); //true equals为value值比较
log.info("128|a1.compareTo(b1)|结果{}",a1.compareTo(b1)); // 0 compareTo也是value值比较
log.info("128|a1.compareTo(c1)|结果{}",a1.compareTo(c1)); // 0 compareTo也是value值比较
log.info("128|c1.compareTo(d1)|结果{}",c1.compareTo(d1)); // 0 compareTo也是value值比较
}
1、Integer ==比较时,要注意如果值不能确定在[-128,127]的缓存区间,不要使用==比较。
三、Integer与String
知道了Integer [-128,127]的缓存区间问题,还是经常踩坑,也是,,,,且看下文源码和测试
//Integer equals
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
//String equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
测试代码
@Test
public void stringTest() {
//integer与String
Integer b = 1; //编译时为Integer.valueOf(1);
Integer c = new Integer(1);
//value = parseInt(s, 10);
Integer d = new Integer("1");
String e = "1";
log.info("b.equals(c)|结果{}", b.equals(c)); //true equals为value值比较
log.info("b.equals(d)|结果{}", b.equals(d)); //true equals为value值比较
log.info("c.equals(d)|结果{}", c.equals(d)); //true equals为value值比较
log.info("b.equals(e)|结果{}", b.equals(e)); //false 我常在这踩坑,虽然编译不报错,但 !(obj instanceof Integer) return flase
log.info("c.equals(e)|结果{}", c.equals(e)); //false 我常在这踩坑,虽然编译不报错,但 !(obj instanceof Integer) return flase
log.info("d.equals(e)|结果{}", d.equals(e)); //false 我常在这踩坑,虽然编译不报错,但 !(obj instanceof Integer) return flase
log.info("b.equals(Integer.valueOf(e))|结果{}", b.equals(Integer.valueOf(e))); //true 如果遇到Integer 与String的比较,一定转成相同类型再equals
log.info("e.equals(b)|结果{}", e.equals(b)); //flase !(anObject instanceof String) return flase
log.info("e.equals(String.valueOf(b))|结果{}", e.equals(String.valueOf(b))); //true 如果遇到Integer 与String的比较,一定转成相同类型再equals
}
测试结果
总结:
防了Integer [-128,127]的缓存区间 == 的问题 ,也要防equals类型不一致的问题,反正我常在这踩坑。
总结:
1、如果是int和Integer,目前高版本会自动拆箱,直接可以==值比较
2、要理解 Integer [-128,127]的缓存区间原理,因为常用Integer的字典,能确定在缓存区间内,就可以用 ==,地址指针一样,都是Integer[] cache 中的
3、由于入参问题,常常会以String接收的参数与字典Integer比较,这时一定要注意保持类型一致后再equals