JDK里那些有趣的代码(1)

前言

今天我们来看一下JDK里一些好玩的代码。我们来分析下。

在Integer源码里,我们可以看到这样一段代码:

1
2
3
4
5
6
7
8
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,99999999, 999999999, Integer.MAX_VALUE };

// Requires positive x
static int stringSize1(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}

这段代码是在干啥???

我们仔细读下,其实就会发现他其实是计算传入的int型x的位数,要求x为正数。

嗯,不难理解。

他为什么要这么写呢?

思考

对于我们来讲,当拿到一个int型正整数,如何用程序算出它的位数呢?

一般不假思索的可能想到String的length方法计算。如下:

1
2
3
static int myStringSize1(int x){
return String.valueOf(x).length();
}

嗯,代码量很少。。。。。。

还有吗?

对,还可以用除法,除以10,计算位数。

1
2
3
4
5
6
7
8
static int myStringSize2(int x){
int num=1;
while(x>10){
x=x/10;
num++;
}
return num;
}

嗯。。。。不错不错~

当然能用除法解决的基本上也可以用乘法解决,int的最大位数为10位,所以可以乘以10,判断大小。

1
2
3
4
5
6
7
8
9
10
// Requires positive x
static int stringSize2(int x) {
int p = 10;
for (int i=1; i<11; i++) {
if (x < p)
return i;
p = 10*p;
}
return 10;
}

好吧,目前我就想到了除JDK以外的这三种方法。

我们测试一下我们的代码正确性。

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
int [] x=new int[]{5,10,333,6666,77777,123456,1234567,87654321,999999999,1111111111,Integer.MAX_VALUE};
for(int i=0;i<x.length;i++){
int a1=Test.stringSize1(x[i]);
int a2=Test.stringSize2(x[i]);
int a3=Test.myStringSize1(x[i]);
int a4=Test.myStringSize2(x[i]);
System.out.println(a1+"---"+a2+"---"+a3+"---"+a4);
}
}

运行结果:

upload successful

结果还是木有问题哒。

提升

JDK的这种写法有什么好处呢???

可以看到,JDK的方法把一部分数据计算变成了数据比较,相当于优化吧。

那它的执行效率怎么样呢?

我们写的方法的效率又如何呢?

我们来测试下吧!

实践

我们可以制造一个随机的定长int数组,看看它们执行耗时,同时统计若干组数据,进行比较。

话不多说,直接写代码并记录到Excel里供分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public static void main(String[] args) throws  Exception{

List<List<Long>> rowList= Lists.newArrayList();

List<String> titleList=Lists.newArrayList();
titleList.add("JDK方法");
titleList.add("乘法");
titleList.add("String方法");
titleList.add("除法");


for(int s=0;s<50;s++){

List<Long> cellList=Lists.newArrayList();

int [] xArrays=new int [10000];
for(int i=0;i<xArrays.length;i++){
xArrays[i]=1 + (int)(Math.random()*Integer.MAX_VALUE);
}
//System.out.println("当前S值为"+s);

long start1=System.nanoTime();
for(int i=0;i<xArrays.length;i++) {
stringSize1(xArrays[i]);
}
long end1=System.nanoTime();
long time1=(end1-start1)/1000;
System.out.println("JDK方法耗时---》"+time1+"ms");
cellList.add(time1);


long start4=System.nanoTime();
for(int i=0;i<xArrays.length;i++) {
stringSize2(xArrays[i]);
}
long end4=System.nanoTime();
long time4=(end4-start4)/1000;
System.out.println("乘法耗时---》"+time4+"ms");
cellList.add(time4);


long start2=System.nanoTime();
for(int i=0;i<xArrays.length;i++) {
myStringSize1(xArrays[i]);
}
long end2=System.nanoTime();
long time2=(end2-start2)/1000;
System.out.println("String方法耗时---》"+time2+"ms");
cellList.add(time2);


long start3=System.nanoTime();
for(int i=0;i<xArrays.length;i++) {
myStringSize2(xArrays[i]);
}
long end3=System.nanoTime();
long time3=(end3-start3)/1000;
System.out.println("除法耗时---》"+time3+"ms");
cellList.add(time3);

rowList.add(cellList);
}

WriteExcelUtil.writeExecl(titleList,rowList,"/Users/zhangwentong/Desktop/workbook.xlsx");
}

我记录了50组数据,每组里面每个计算位数的方法执行10000次,得到如下结果。

upload successful

把它绘制成折线图。

upload successful

可以看到,String的length方法效率是最差的。。。。

我们其实看一下String.valueOf(x).length()这个源码,就知道为什么这么慢了。

然后除法也稍微逊色一点。。。

乘法和JDK的效率都可以说不错。。。。。但JDK方法其实要好一点。。。。

而且开始创建的sizeTable是占据较少空间,但却降低了CPU的计算次数(乘法需要每次乘以十在比较计算)。

当然,因为int的位数只有10位,可以写一个sizeTable,当数据量大时,比如long,最大19位,写一个19个数的sizeTable? 一堆9???

哈哈,我们可以看看Long里面的计算位数的方法。

1
2
3
4
5
6
7
8
9
10
// Requires positive x
static int stringSize(long x) {
long p = 10;
for (int i=1; i<19; i++) {
if (x < p)
return i;
p = 10*p;
}
return 19;
}

人家当然用的乘法啦。。。。。

结论

我们可以看到,我们最容易想到的String.length方法确是效率最低的。。。。

JDK源码里一些方法会被大量调用,当然要做到最好的优化啦。。。

多读些源码,深入思考,多加练习,有助于提升自己。

今天就到这儿吧。




-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道