Java中substring、split、StringTokenizer三种截取字符串方法的性能比较

最近在阅读java.lang下的源码,读到String时,突然想起面试的时候曾经被人问过:都知道在大数据量情况下,使用String的split截取字符串效率很低,有想过用其他的方法替代吗?用什么替代?我当时的回答很斩钉截铁:没有。
Google了一下,发现有2种替代方法,于是在这里我将对这三种方式进行测试。
测试的软件环境为:Windows 10、Intellij IDEA、JDK1.8。
测试用例使用类ip形式的字符串,即3位一组,使用”.”间隔。数据分别使用:5组、10组、100组、1000组、10000组、100000组。

实现

闲话不说,先上代码:

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

/**
 * String测试类
 * @author yuitang
 *
 */
public class StringTest {

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

        //////////////String.split()表现//////////////////////////////////////////////
        System.out.println("使用String.split()切分字符串");
        long st1 = System.nanoTime();
        String [] result = orginStr.split("\\.");
        System.out.println("String.split()截取字符串用时:" + (System.nanoTime()-st1));
        System.out.println("String.split()截取字符串结果个数:" + 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组测试数据结果如下图:

Function510100100010000100000
split45792501341555341035446539513623128381
StringTokenizer240802487041845627664460830002
substring3552837896156324838463857529325692324

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

TablessplitStringTokenizersubstring
51.901.29
102.021.32
1003.170.99
100016.501.23
10000120.950.63
100000770.890.90

结论

最终,StringTokenizer在截取字符串中效率最高,不论数据量大小,几乎持平。substring和split的效率几乎差别不大,甚至当数据量足够庞大的时候,substring的效率还比不上split。
究其原因,split的实现方式是采用正则表达式实现,所以其性能会比较低。至于正则表达式为何低,还未去验证。split源码如下:

public String[] split(String regex) {
    return split(regex, 0);
}

public String[] split(String regex, int limit) {
    /* fastpath if the regex is a
     (1)one-char String and this character is not one of the
        RegEx's meta characters ".$|()[{^?*+\\", or
     (2)two-char String and the first char is the backslash and
        the second is not the ascii digit or ascii letter.
     */
    char ch = 0;
    if (((regex.value.length == 1 &&
         ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
         (regex.length() == 2 &&
          regex.charAt(0) == '\\' &&
          (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
          ((ch-'a')|('z'-ch)) < 0 &&
          ((ch-'A')|('Z'-ch)) < 0)) &&
        (ch < Character.MIN_HIGH_SURROGATE ||
         ch > Character.MAX_LOW_SURROGATE))
    {
        int off = 0;
        int next = 0;
        boolean limited = limit > 0;
        ArrayList<String> list = new ArrayList<>();
        while ((next = indexOf(ch, off)) != -1) {
            if (!limited || list.size() < limit - 1) {
                list.add(substring(off, next));
                off = next + 1;
            } else {    // last one
                //assert (list.size() == limit - 1);
                list.add(substring(off, value.length));
                off = value.length;
                break;
            }
        }
        // If no match was found, return this
        if (off == 0)
            return new String[]{this};

        // Add remaining segment
        if (!limited || list.size() < limit)
            list.add(substring(off, value.length));

        // Construct result
        int resultSize = list.size();
        if (limit == 0) {
            while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                resultSize--;
            }
        }
        String[] result = new String[resultSize];
        return list.subList(0, resultSize).toArray(result);
    }
    return Pattern.compile(regex).split(this, limit);
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值