一、简介
我们都知道Integer是int的包装类。一定情况下,jdk会在两者之间自动装箱与拆箱操作。
也知道Integer是包装类,是类;int是8大基本数据类型中的一种。
除此之外他们也有很多其他的不同,这里就不过多的介绍了,有兴趣的可以百度查下。
在此谈谈数据溢出、Integer缓存问题。
二、2个问题
1、取2个int数据的中间值
我们要取2个int数据的中间值,一般会像如下代码操作,一般情况没有问题的。
public static void main(String[] args) {
getMiddle(100, 200);// 150
}
// 输出2个值的中间值
private static void getMiddle(int a, int b) {
System.out.println((a + b) / 2);
}
如果我的a与b都是int类型的最大值呢?结果是 -1 ? 是的,结果不是预期的结果,因为发生了数据溢出。
public static void main(String[] args) {
System.out.println("int型最大值是:" + Integer.MAX_VALUE + 1);// 2147483647
getMiddle(Integer.MAX_VALUE, Integer.MAX_VALUE);// -1
}
// 输出2个值的中间值
private static void getMiddle(int a, int b) {
System.out.println((a + b) / 2);
}
怎么解决呢?一般我们有2种解决方式,如下:
- 使用一些小算法:
取中间值等价于 ==> a与b差的绝对值 / 2 + 最小值 ==> |( a-b)| / 2 + min
public static void main(String[] args) {
System.out.println("int型最大值是:" + Integer.MAX_VALUE + 1);// 2147483647
getMiddle2(Integer.MAX_VALUE, Integer.MAX_VALUE);// 2147483647
}
private static void getMiddle2(int a, int b) {
// a与b差的绝对值 / 2 + 最小值
System.out.println(Math.abs(a - b) / 2 + Math.min(a, b));
}
- 使用更大的数值类型(eg:long)
public static void main(String[] args) {
System.out.println("int型最大值是:" + Integer.MAX_VALUE + 1);// 2147483647
getMiddle3(Integer.MAX_VALUE, Integer.MAX_VALUE);// 2147483647
}
// 会自动提升数据类型:int -> long
private static void getMiddle3(long a, long b) {
System.out.println((a + b) / 2);
}
2、Integer数值比较
下方一组数值的比较以及运行结果:
public static void main(String[] args) {
int a = 2;
int b = 2;
Integer c = 2;
Integer d = 2;
Integer e = new Integer(2);
Integer f = new Integer(2);
Integer g = 128;
Integer h = 128;
/* 注意:有a或者b参与的比较,只要数值相同则结果相等,可以忽略对象这个属性,可以认为只比较数值。
因此,a == c 是true;a == e 是true。
*/
System.out.println(a == b);// true
// 因为 c 与 d 是在integer的范围中的,因此装箱后取得是同一个对象的引用
System.out.println(c == d);// true
// 因为 g 与 h 超过了缓存的范围,所以需要重新 new,因此是2个不同对象
System.out.println(g == h);// false
// 使用new创建的对象,堆内存开辟了2个空间,是2个对象
System.out.println(e == f);// false
System.out.println(a == c);// true
System.out.println(c == a);// true
System.out.println(a == e);// true
// c是缓存好的数据,e是new出来的,是2个不同对象
System.out.println(c == e);// false
}
分析原因:主要就是Integer内部有一个内部类以及装箱拆箱机制,造成如此结果。
在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。另外IntegerCache会缓存[-128, 127]的int数据,造成对象相等。
下面是Integer的部分源代码:
/**
* 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);
}
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() {}
}
三、总结
平时在使用integer的时候,要注意一些细节,否则把错误留到生产环境就得不偿失了。我们应该留意如下2点:
- Int / Integer 数据操作是否会产生数值溢出
- 使用Integer做比较的时候,应当谨记Integer内部的缓存数组(范围: [-128, 127]),避免得不到期望结果
本文暂时没有评论,来添加一个吧(●'◡'●)