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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。