好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java中自动装箱、拆箱引起的耗时详解

什么是自动装箱,拆箱

先抛出定义,java中基础数据类型与它们的包装类进行运算时,编译器会自动帮我们进行转换,转换过程对程序员是透明的,这就是装箱和拆箱,装箱和拆箱可以让我们的代码更简洁易懂

耗时问题

在说 java 的自动装箱和自动拆箱之前,我们先看一个例子。

这个错误我在项目中犯过(尴尬),拿出来共勉!

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

private static long getcounterresult() {

  long sum = 0l;

  final int length = integer.max_value;

  for ( int i = 0 ; i < length; i++) {

  sum += i;

  }

  return sum;

}

public static void main(string[] args) {

  long startcounttime = system.currenttimemillis();

  long result = getcounterresult();

  long endcounttime = system.currenttimemillis();

  system.out.println( "result = " + result + ", and take up time : " + (endcounttime - startcounttime) / 1000 + "s" );

}

在我的电脑(macos 64位系统,配置较高),打印结果如下:

result = 2305843005992468481, and take up time : 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

 

自动装箱示例:

?

1

2

int a = 3 ;

integer b = a;

自动拆箱示例:

?

1

2

integer b = new integer( 7 );

int a = b;

integer/int 自动拆箱和装箱

下面这段代码是 integer 的源码中 valueof 方法。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

/**

  * 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) {

  // 如果i的值大于-128小于127则返回一个缓冲区中的一个integer对象

  if (i >= integercache.low && i <= integercache.high)

  return integercache.cache[i + (-integercache.low)];

 

  // 否则返回 new 一个integer 对象

  return new integer(i);

}

我们在执行下面的这句代码,如下:

?

1

integer i = 100 ;

上面的代码等同于下面的代码:

?

1

integer i = integer.valueof( 100 );

结合上面的源码可以看出来,如果数值在 [-128,127] 之间(双闭区间),不会重新创建 integer 对象,而是从缓存中(常量池)直接获取,从常量池中获取而不是堆栈操作,读取数据要快很多。

我们再来看一下常见的基础面试题(请给出打印结果),如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public static void main(string[] args) {

  // ?

  integer a = new integer( 121 );

  integer b = new integer( 121 );

  system.out.println(a == b);

 

  // ?

  integer c = 121 ;

  integer d = 121 ;

  system.out.println(c == d);

 

  // ?

  integer e = 129 ;

  integer f = 129 ;

  system.out.println(e == f);

 

  // ?

  int g = 50 ;

  integer h = new integer( 50 );

  system.out.println(g == h);

}

分析结果:

?: false, 两个对象进行比较分别指向了不同堆内存

?: true, 自动装箱且数值在 [-128,127] 之间(双闭区间)

?: false, 自动装箱且数值不在 [-128,127] 之间(双闭区间)

?: true, 自动拆箱且数值在 [-128,127] 之间(双闭区间)

解析耗时问题

类 long 对应的也有一个 valueof 方法,源码如下:

?

1

2

3

4

5

6

7

public static long valueof( long l) {

  final int offset = 128 ;

  if (l >= - 128 && l <= 127 ) { // will cache

   return longcache.cache[( int )l + offset];

  }

  return new long (l);

}

这个和 integer 的很像,道理上面说过,这里不再赘述。

在开篇的例子中,getcounterresult 方法有下面这句代码,如下:

?

1

long sum = 0l;

很明显我们声明了一个 long 的对象 sum,由于自动装箱,这句代码并没有语法上面的错误,编译器当然也不会报错。上面代码等同于如下代码:

?

1

long sum = long .valueof( 0 );

在 for 循环中,超过 [-128,127] 就会创建新的对象,这样不断的创建对象,不停的申请堆内存,程序执行自然也就比较耗时了。

修改一下代码,如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

private static long getcounterresult() {

  // 修改为普通的基本类型数据

  long sum = 0l;

  final int length = integer.max_value;

  for ( int i = 0 ; i < length; i++) {

   sum += i;

  }

  return sum;

}

public static void main(string[] args) {

  long startcounttime = system.currenttimemillis();

  long result = getcounterresult();

  long endcounttime = system.currenttimemillis();

  system.out.println( "result = " + result + ", and take up time : " + (endcounttime - startcounttime) / 1000 + "s" );

}

执行时间大大缩短。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。

原文链接:http://HdhCmsTestveryitman测试数据/2019/04/07/Java-自动装箱、拆箱引起的耗时/

查看更多关于Java中自动装箱、拆箱引起的耗时详解的详细内容...

  阅读:18次