Java中的StringBuilder性能测试

作者:junjie 时间:2021-09-11 03:38:20 

在看KMP算法时,想要简单的统计一下执行时间和性能。

得出的结论是: Java的String的indexOf方法性能最好,其次是KMP算法,其次是传统的BF算法,当然,对比有点牵强,SUN的算法也使用Java来实现、用的看着不像是KMP,还需要详细研究一下。

测试代码如下所示:


package com.test.test.kmp;

import java.util.Random;

public class KMPTest {

public static void main(String[] args) {
test();
}

//
public static void test() {
//
int allLen = 8000000;
int subLen = 11;
int charLen = 4;
String cl = buildString(charLen);
int times = 50;
//
testMatch(allLen, subLen, cl, times);
}

public static void testMatch(int allLen, int subLen, String cl, int times){
int allBF = 0;
int allString = 0;
int allKMP = 0;
int allGC = 0;
int allbuild = 0;
int alltoArray = 0;

long start = System.currentTimeMillis();

System.out.println("--------------------------");
for (int i = 0; i < times; i++) {
// 1. 构造字符串的损耗
long buildStart = System.currentTimeMillis();
String sub = buildString(subLen, cl);
String all = buildString(allLen, sub) +sub;
long buildEnd = System.currentTimeMillis();
allbuild += (buildEnd - buildStart);
// 2. toCharArray的损耗
long toArrayStart = System.currentTimeMillis();
char[] allArray = all.toCharArray();
char[] subArray = sub.toCharArray();
long toArrayEnd = System.currentTimeMillis();
alltoArray += (toArrayEnd - toArrayStart);
//
long startBF = System.currentTimeMillis();
int indexBF = bfIndexOf(all, sub);
long endBF = System.currentTimeMillis();
//
long timeoutBF = endBF - startBF;
allBF += timeoutBF;
System.gc();
allGC += (System.currentTimeMillis() - endBF);

//
long startString = System.currentTimeMillis();
int indexString = stringIndexOf(all, sub);
long endString = System.currentTimeMillis();
//
long timeoutString = endString - startString;
allString += timeoutString;
System.gc();
allGC += (System.currentTimeMillis() - endString);

//
long startKMP = System.currentTimeMillis();
//int indexKMP = kmpIndexOf(all, sub);
int indexKMP = KMP_Index(allArray, subArray);
long endKMP = System.currentTimeMillis();
//
long timeoutKMP = endKMP - startKMP;
allKMP += timeoutKMP;
System.gc();
allGC += (System.currentTimeMillis() - endKMP);

//
//System.out.println("all="+all.substring(0,100)+" ......");
//System.out.println("sub="+sub);
//
//System.out.println("indexBF="+indexBF+";耗时: "+timeoutBF+"ms");
//System.out.println("indexString="+indexString+";耗时: "+timeoutString+"ms");
if(indexBF == indexString && indexKMP == indexString){
//System.out.println("!!!!对比相等。");
} else {
throw new RuntimeException("对比出错.");
}

//
if(i % 20 == 10){
System.out.println(i+"次bfIndexOf= "+allBF+"ms");
System.out.println(i+"次stringIndexOf= "+allString+"ms");
System.out.println(i+"次KMPIndexOf= "+allKMP+"ms");
System.out.println(i+"次allbuild= "+allbuild+"ms");
System.out.println(i+"次alltoArray= "+alltoArray+"ms");
System.out.println(i+"*3次,GC= "+allGC+"ms");
long end = System.currentTimeMillis();
long allTime = end-start;
long lossTime = allTime - (allBF+allString+allKMP+allGC);
System.out.println(i+"次,所有总计耗时: "+(allTime)+"ms; 损耗:" + lossTime +"ms; 损耗比: " +((0.0+lossTime)/allTime * 100)+"%");
System.out.println("--------------------------");
}

}
//
System.out.println(times+"次bfIndexOf;总计耗时: "+allBF+"ms");
System.out.println(times+"次stringIndexOf;总计耗时: "+allString+"ms");
System.out.println(times+"次KMPIndexOf;总计耗时: "+allKMP+"ms");
System.out.println(times+"次allbuild= "+allbuild+"ms");
System.out.println(times+"次alltoArray= "+alltoArray+"ms");
System.out.println(times+"*3次,GC;总计耗时: "+allGC+"ms");
long end = System.currentTimeMillis();
long allTime = end-start;
long lossTime = allTime - (allBF+allString+allKMP+allGC);
System.out.println(times+"次,所有总计耗时: "+(allTime)+"ms; 损耗:" + lossTime +"ms; 损耗比: " +((0.0+lossTime)/allTime * 100)+"%");
System.out.println("--------------------------");

}

//

// 构建字符

public static String buildString(int len){
return buildString(len, null);
}
public static String buildString(int len, String sub){
//
final int TYPE_NUMBER = 0;
final int TYPE_LOWERCASE = 1;
final int TYPE_UPPERCASE = 2;
//
long seed = System.nanoTime();
Random random = new Random(seed);
//
//
StringBuilder builder = new StringBuilder();
for (int i = 0; i < len; i++) {
//
int type = random.nextInt(3);// 0-2
int cvalue = 0;
if(TYPE_NUMBER == type){
cvalue = '0' + random.nextInt(10);// 0~(n-1)
} else if(TYPE_LOWERCASE == type){
cvalue = 'a' + random.nextInt(26);// 0~(n-1)
} else if(TYPE_UPPERCASE == type){
cvalue = 'A' + random.nextInt(26);// 0~(n-1)
} else {
throw new RuntimeException(Random.class.getName()+"#newxtInt(int);Wrong!");
}
//
//cvalue = 'A' + random.nextInt(26);// 0~(n-1)
//
char c = (char)cvalue;
if(null != sub && sub.length() > 1){
int index = random.nextInt(sub.length());
c = sub.charAt(index);
}
//String s = String.valueOf(c);
builder.append(c);
//
}
//
return builder.toString();
}

/**
* Java自带的方法,内部实现了
* @param all
* @param sub
* @return
*/
public static int stringIndexOf(String all, String sub){
// 防御式编程
if(null == all || null== sub){
return -1;
}
// 调用Java的String方法
return all.indexOf(sub);
}

/**
* BF算法
* @param all
* @param sub
* @return
*/
public static int bfIndexOf(String all, String sub){
// 防御式编程
if(null == all || null== sub){
return -1;
}
//
int lenAll = all.length();
int lenSub = sub.length();
int i = 0;
while (i < lenAll) {
// 从i下标开始对比
int j = 0;
while (j < lenSub) {
//
char all_i = all.charAt(i);
char sub_j = sub.charAt(j);
if(all_i == sub_j){
// 相等则继续对比下一个字符
i++;
j++; // 这个增长很重要
} else {
// 否则跳出内层循环
break;
}
}
// 如果 j 增长到和 lenSub相等,则匹配成功
if(lenSub == j){
return i - lenSub;
} else {
i = 1+i - j; // 回溯 i
}
}
//
return -1;
}

/**
* KMP 算法
* @param all
* @param sub
* @return
*/
public static int kmpIndexOf(String all, String sub){
//
char[] allArray = all.toCharArray();
char[] subArray = sub.toCharArray();
//
return KMP_Index(allArray, subArray);
}
 /**
  * 获得字符串的next函数值
  *
  * @param t
  *      字符串
  * @return next函数值
  */
 public static int[] next(char[] t) {
   int[] next = new int[t.length];
   next[0] = -1;
   int i = 0;
   int j = -1;
   while (i < t.length - 1) {
     if (j == -1 || t[i] == t[j]) {
       i++;
       j++;
       if (t[i] != t[j]) {
         next[i] = j;
       } else {
         next[i] = next[j];
       }
     } else {
       j = next[j];
     }
   }
   return next;
 }

/**
  * KMP匹配字符串
  *
  * @param allArray
  *      主串
  * @param subArray
  *      模式串
  * @return 若匹配成功,返回下标,否则返回-1
  */
public static int KMP_Index(char[] allArray, char[] subArray) {
   int[] next = next(subArray);
   int i = 0;
   int j = 0;
   while (i <= allArray.length - 1 && j <= subArray.length - 1) {
     if (j == -1 || allArray[i] == subArray[j]) {
       i++;
       j++;
     } else {
       j = next[j];
     }
   }
   if (j < subArray.length) {
     return -1;
   } else
     return i - subArray.length; // 返回模式串在主串中的头下标
 }
}

测试的一个结果如下所示:


--------------------------
10次bfIndexOf= 94ms
10次stringIndexOf= 56ms
10次KMPIndexOf= 76ms
10次allbuild= 5870ms
10次alltoArray= 137ms
10*3次,GC= 155ms
10次,所有总计耗时: 6388ms; 损耗:6007ms; 损耗比: 94.03569192235442%
--------------------------
30次bfIndexOf= 365ms
30次stringIndexOf= 222ms
30次KMPIndexOf= 295ms
30次allbuild= 16452ms
30次alltoArray= 395ms
30*3次,GC= 452ms
30次,所有总计耗时: 18184ms; 损耗:16850ms; 损耗比: 92.66388033435987%
--------------------------
50次bfIndexOf;总计耗时: 550ms
50次stringIndexOf;总计耗时: 335ms
50次KMPIndexOf;总计耗时: 450ms
50次allbuild= 26501ms
50次alltoArray= 637ms
50*3次,GC;总计耗时: 734ms
50次,所有总计耗时: 29211ms; 损耗:27142ms; 损耗比: 92.91705179555647%
--------------------------

从中可以看出来,使用StringBuilder构造字符串,800万*50次append大约消耗了26秒。换算下来每秒钟是1600万次。。。
看来是需要写一个专门计时的函数,本来Junit是有自己的统计的,但是样本不太好做。

如此看来,数据的准备,也就是测试用例的选取很关键,可能需要先生成并写入到文本文件中。

标签:Java,StringBuilder,性能测试
0
投稿

猜你喜欢

  • Android重力传感器实现滚动的弹球

    2023-05-04 05:49:28
  • Android SeekBar充当Progress实现兔兔进度条Plus

    2021-12-05 16:36:25
  • Java毕业设计实战项目之宠物商城系统的实现流程

    2022-03-02 06:10:41
  • Java中过滤器 (Filter) 和 拦截器 (Interceptor)的使用

    2023-07-07 00:20:28
  • java多线程和并发包入门示例

    2022-05-10 12:29:34
  • 本地编译打包项目部署到服务器并且启动方式

    2022-02-18 06:27:45
  • Java调用WebService接口作测试

    2023-08-11 17:00:20
  • C#深浅拷贝的深入解析

    2023-03-28 18:36:28
  • Java Web开发过程中登陆模块的验证码的实现方式总结

    2022-01-29 19:33:16
  • Java中IO流解析及代码实例详解

    2022-03-08 22:32:16
  • 浅谈JAVA 内存流的实现

    2021-06-28 05:43:59
  • Android Rsa数据加解密的介绍与使用示例

    2023-06-24 04:51:38
  • Scala异常处理的方法深入分析

    2022-01-09 19:50:35
  • MTK Android平台开发流程

    2023-06-23 08:59:11
  • QT5实现简单的TCP通信的实现

    2023-11-02 21:24:48
  • java8新特性-lambda表达式入门学习心得

    2021-09-26 17:14:33
  • 详细总结Java堆栈内存、堆外内存、零拷贝浅析与代码实现

    2023-11-21 01:52:29
  • java面试题——详解HashMap和Hashtable 的区别

    2023-08-06 16:38:25
  • Android Retrofit的使用详解

    2022-12-11 01:28:37
  • Android禁止EditText自动弹出软键盘的方法及遇到问题

    2021-07-18 06:35:55
  • asp之家 软件编程 m.aspxhome.com