Java中自动装箱、拆箱引起的耗时详解
什么是自动装箱,拆箱
先抛出定义,Java中基础数据类型与它们的包装类进行运算时,编译器会自动帮我们进行转换,转换过程对程序员是透明的,这就是装箱和拆箱,装箱和拆箱可以让我们的代码更简洁易懂
耗时问题
在说Java的自动装箱和自动拆箱之前,我们先看一个例子。
这个错误我在项目中犯过(尴尬),拿出来共勉!
privatestaticlonggetCounterResult(){
Longsum=0L;
finalintlength=Integer.MAX_VALUE;
for(inti=0;i
在我的电脑(macOS64位系统,配置较高),打印结果如下:
result=2305843005992468481,andtakeuptime:12s
居然使用了12s,是可忍叔不可忍,再正常不过的代码怎么会耗时这么久呢?如果在配置差一点的电脑上运行耗时会更久(惊呆了.jpg)。
我们不妨先阅读下面的内容,再来分析、解决上述耗时的问题。
基本概念
自从jdk1.5之后就有了自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)。
自动装箱,就是Java自动将原始(基本)类型转换成对应的封装器(对象)类型的过程,比如将int的变量转换成Integer对象,这个过程叫做装箱。
自动拆箱,就是Java自动将封装器(对象)类型转换成基本类型的过程,如将Integer对象转换成int类型值,这个过程叫做拆箱。
之所以称之为自动装箱和拆箱,是因为这些操作并非人工(程序猿)操作的,而是Java自带的一个特性。
下表是Java中的基本类型和对应的封装类型的对应表:
基本类型
封装器类
int
Integer
byte
Byte
long
Long
float
float
double
Double
char
Character
boolean
Boolean
自动装箱示例:
inta=3;
Integerb=a;
自动拆箱示例:
Integerb=newInteger(7);
inta=b;
Integer/int自动拆箱和装箱
下面这段代码是Integer的源码中valueOf方法。
/**
*Returnsan{@codeInteger}instancerepresentingthespecified
*{@codeint}value.Ifanew{@codeInteger}instanceisnot
*required,thismethodshouldgenerallybeusedinpreferenceto
*theconstructor{@link#Integer(int)},asthismethodislikely
*toyieldsignificantlybetterspaceandtimeperformanceby
*cachingfrequentlyrequestedvalues.
*
*Thismethodwillalwayscachevaluesintherange-128to127,
*inclusive,andmaycacheothervaluesoutsideofthisrange.
*
*@paramian{@codeint}value.
*@returnan{@codeInteger}instancerepresenting{@codei}.
*@since1.5
*/
publicstaticIntegervalueOf(inti){
//如果i的值大于-128小于127则返回一个缓冲区中的一个Integer对象
if(i>=IntegerCache.low&&i<=IntegerCache.high)
returnIntegerCache.cache[i+(-IntegerCache.low)];
//否则返回new一个Integer对象
returnnewInteger(i);
}
我们在执行下面的这句代码,如下:
Integeri=100;
上面的代码等同于下面的代码:
Integeri=Integer.valueOf(100);
结合上面的源码可以看出来,如果数值在[-128,127]之间(双闭区间),不会重新创建Integer对象,而是从缓存中(常量池)直接获取,从常量池中获取而不是堆栈操作,读取数据要快很多。
我们再来看一下常见的基础面试题(请给出打印结果),如下:
publicstaticvoidmain(String[]args){
//⓵
Integera=newInteger(121);
Integerb=newInteger(121);
System.out.println(a==b);
//⓶
Integerc=121;
Integerd=121;
System.out.println(c==d);
//⓷
Integere=129;
Integerf=129;
System.out.println(e==f);
//⓸
intg=50;
Integerh=newInteger(50);
System.out.println(g==h);
}
分析结果:
⓵:false,两个对象进行比较分别指向了不同堆内存
⓶:true,自动装箱且数值在[-128,127]之间(双闭区间)
⓷:false,自动装箱且数值不在[-128,127]之间(双闭区间)
⓸:true,自动拆箱且数值在[-128,127]之间(双闭区间)
解析耗时问题
类Long对应的也有一个valueof方法,源码如下:
publicstaticLongvalueOf(longl){
finalintoffset=128;
if(l>=-128&&l<=127){//willcache
returnLongCache.cache[(int)l+offset];
}
returnnewLong(l);
}
这个和Integer的很像,道理上面说过,这里不再赘述。
在开篇的例子中,getCounterResult方法有下面这句代码,如下:
Longsum=0L;
很明显我们声明了一个Long的对象sum,由于自动装箱,这句代码并没有语法上面的错误,编译器当然也不会报错。上面代码等同于如下代码:
Longsum=Long.valueof(0);
在for循环中,超过[-128,127]就会创建新的对象,这样不断的创建对象,不停的申请堆内存,程序执行自然也就比较耗时了。
修改一下代码,如下:
privatestaticlonggetCounterResult(){
//修改为普通的基本类型数据
longsum=0L;
finalintlength=Integer.MAX_VALUE;
for(inti=0;i
执行时间大大缩短。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。