java中subString、split、stringTokenizer三种截取字符串方法的性能比较



java中subString、split、stringTokenizer三种截取字符串方法的性能比较。最近在阅读java.lang下的源码,读到String时,突然想起面试的时候曾经被人问过:都知道在大数据量情况下,使用String的split截取字符串效率很低,有想过用其他的方法替代吗?用什么替代?我当时的回答很斩钉截铁:没有。

google了一下,发现有2中替代方法,于是在这里我将对这三种方式进行测试。

测试的软件环境为:Windows XP、eclipse、JDK1.6。

测试用例使用类ip形式的字符串,即3位一组,使用”.”间隔。数据分别使用:5组、10组、100组、1000组、10000组、100000组。

实现

闲话不说,先上代码:

  1. package test.java.lang.ref;
  2. import java.util.Random;
  3. import java.util.StringTokenizer;
  4. /**
  5. * String测试类
  6. * @author xiaori.Liu
  7. *
  8. */
  9. public class StringTest {
  10.     public static void main(String args[]){
  11.         String orginStr = getOriginStr(10);
  12.         //////////////String.splic()表现//////////////////////////////////////////////
  13.         System.out.println(“使用String.splic()的切分字符串”);
  14.         long st1 = System.nanoTime();
  15.         String [] result = orginStr.split(“\\.”);
  16.         System.out.println(“String.splic()截取字符串用时:” + (System.nanoTime()-st1));
  17.         System.out.println(“String.splic()截取字符串结果个数:” + result.length);
  18.         System.out.println();
  19.         //////////////StringTokenizer表现//////////////////////////////////////////////
  20.         System.out.println(“使用StringTokenizer的切分字符串”);
  21.         long st3 = System.nanoTime();
  22.         StringTokenizer token=new StringTokenizer(orginStr,”.”);
  23.         System.out.println(“StringTokenizer截取字符串用时:”+(System.nanoTime()-st3));
  24.         System.out.println(“StringTokenizer截取字符串结果个数:” + token.countTokens());
  25.         System.out.println();
  26.         ////////////////////String.substring()表现//////////////////////////////////////////
  27.         long st5 = System.nanoTime();
  28.         int len = orginStr.lastIndexOf(“.”);
  29.         System.out.println(“使用String.substring()切分字符串”);
  30.         int k=0,count=0;
  31.         for (int i = 0; i <= len; i++) {
  32.          if(orginStr.substring(i, i+1).equals(“.”)){
  33.           if(count==0){
  34.            orginStr.substring(0, i);
  35.           }else{
  36.              orginStr.substring(k+1, i);
  37.              if(i == len){
  38.                orginStr.substring(len+1, orginStr.length());
  39.            }
  40.           }
  41.           k=i;count++;
  42.          }
  43.         }
  44.         System.out.println(“String.substring()截取字符串用时”+(System.nanoTime()-st5));
  45.         System.out.println(“String.substring()截取字符串结果个数:” + (count + 1));
  46.     }
  47.     /**
  48.      * 构造目标字符串
  49.      * eg:10.123.12.154.154
  50.      * @param len 目标字符串组数(每组由3个随机数组成)
  51.      * @return
  52.      */
  53.     private static String getOriginStr(int len){
  54.         StringBuffer sb = new StringBuffer();
  55.         StringBuffer result = new StringBuffer();
  56.         Random random = new Random();
  57.         for(int i = 0; i < len; i++){
  58.             sb.append(random.nextInt(9)).append(random.nextInt(9)).append(random.nextInt(9));
  59.             result.append(sb.toString());
  60.             sb.delete(0, sb.length());
  61.             if(i != len-1)
  62.                 result.append(“.”);
  63.         }
  64.         return result.toString();
  65.     }
  66. }
package test.java.lang.ref;

import java.util.Random;
import java.util.StringTokenizer;

/**
 * String测试类
 * @author xiaori.Liu
 *
 */
public class StringTest {

    public static void main(String args[]){
        String orginStr = getOriginStr(10);

        //////////////String.splic()表现//////////////////////////////////////////////
        System.out.println("使用String.splic()的切分字符串"); 
        long st1 = System.nanoTime(); 
        String [] result = orginStr.split("\\.");
        System.out.println("String.splic()截取字符串用时:" + (System.nanoTime()-st1));
        System.out.println("String.splic()截取字符串结果个数:" + result.length);
        System.out.println();

        //////////////StringTokenizer表现//////////////////////////////////////////////
        System.out.println("使用StringTokenizer的切分字符串"); 
        long st3 = System.nanoTime();  
        StringTokenizer token=new StringTokenizer(orginStr,".");  
        System.out.println("StringTokenizer截取字符串用时:"+(System.nanoTime()-st3)); 
        System.out.println("StringTokenizer截取字符串结果个数:" + token.countTokens());
        System.out.println();

        ////////////////////String.substring()表现//////////////////////////////////////////

        long st5 = System.nanoTime();  
        int len = orginStr.lastIndexOf(".");
        System.out.println("使用String.substring()切分字符串");  
        int k=0,count=0;  

        for (int i = 0; i <= len; i++) {  
         if(orginStr.substring(i, i+1).equals(".")){  
          if(count==0){  
           orginStr.substring(0, i);  
          }else{  
             orginStr.substring(k+1, i); 
             if(i == len){
               orginStr.substring(len+1, orginStr.length()); 
           }
          }
          k=i;count++;  
         }  
        }
        System.out.println("String.substring()截取字符串用时"+(System.nanoTime()-st5));  
        System.out.println("String.substring()截取字符串结果个数:" + (count + 1));
    }

    /**
     * 构造目标字符串
     * eg:10.123.12.154.154
     * @param len 目标字符串组数(每组由3个随机数组成)
     * @return
     */
    private static String getOriginStr(int len){

        StringBuffer sb = new StringBuffer();
        StringBuffer result = new StringBuffer();
        Random random = new Random();
        for(int i = 0; i < len; i++){
            sb.append(random.nextInt(9)).append(random.nextInt(9)).append(random.nextInt(9));
            result.append(sb.toString());
            sb.delete(0, sb.length());
            if(i != len-1)
                result.append(".");
        }

        return result.toString();
    }
}

改变目标数据长度修改getOriginStr的len参数即可。

5组测试数据结果如下图:

下面这张图对比了下,split耗时为substring和StringTokenizer耗时的倍数:

好吧,我又花了点儿时间,做了几张图表来分析这3中方式的性能。

首先来一张柱状图对比一下这5组数据截取所花费的时间:


从上图可以看出StringTokenizer的性能实在是太好了(对比另两种),几乎在图表中看不见它的身影。遥遥领先。substring花费的时间始终比split要少,但是耗时也在随着数据量的增加而增加。

下面3张折线图可以很明显看出split、substring、StringTokenizer3中实现随着数据量增加,耗时的趋势。

split是变化最大的,也就是数据量越大,截取所需要的时间增长越快。

substring则比split要平稳一点点,但是也在增长。

StringTokenizer则是表现最优秀的,基本上平稳,始终保持在5000ns一下。

结论

最终,StringTokenizer在截取字符串中效率最高,不论数据量大小,几乎持平。substring则要次之,数据量增加耗时也要随之增加。split则是表现最差劲的。

究其原因,split的实现方式是采用正则表达式实现,所以其性能会比较低。至于正则表达式为何低,还未去验证。split源码如下:

  1. public String[] split(String regex, int limit) {
  2.     return Pattern.compile(regex).split(this, limit);
  3. }
public String[] split(String regex, int limit) {
    return Pattern.compile(regex).split(this, limit);
}