字符串切割

4/3/2022 Java

摘要

JDK:1.8.0_202

# 一:前言

下面分享几种常见的字符串切割方法,以及他们的耗时,

首先定义一个生成超长字符串的工具类,方便后面测试时使用

public class StringGeneratorUtils {

    /**
     * 生成一个用逗号分隔的超长字符串,从 0 开始一直到 9999 的每个数字都用逗号分隔
     *
     * @return 超长字符串
     */
    public static String generate() {
        StringBuilder stringBuffer = new StringBuilder();

        int max = 10000;
        for (int i = 0; i < max; i++) {
            stringBuffer.append(i);
            if (i < max - 1) {
                stringBuffer.append(",");
            }
        }

        return stringBuffer.toString();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

下面所有的例子中,因为单次切割时间太短,所以按照100次切割,作为一个单位时间,观察循环记录 50 次,可以看到他们的平均耗时。

同时由于真实的操作中,往往会对切割后,做一些其他操作,例如遍历,判断长度等等,为了更好模拟,下面的所有结果,都会打印第一个数字和最后一个数字。

# 二:String

使用最基础的split方法来对超长字符串做切割,如下代码测试:

public class StringSplitTest {

    public static void main(String[] args) {
        // 获取一个超长字符串
        String string = StringGeneratorUtils.generate();
        List<Long> list = new ArrayList<>(21);

        for (int i = 1; i < 21; i++) {
            System.out.printf("第%d次切割,\t", i);
            list.add(extracted(string));
        }

        System.out.printf("平均耗时:%s", list.stream().mapToLong(Long::longValue).average().getAsDouble());
    }

    private static long extracted(String string) {
        String[] strings = null;
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            // 切割字符串
            strings = string.split(",");
        }
        System.out.printf("切割耗时:%d,", System.currentTimeMillis() - start);
        System.out.printf("查找第一个数字:%s,查找最后一个数字:%s,", strings[0], strings[strings.length - 1]);
        long allTime = System.currentTimeMillis() - start;
        System.out.printf("总耗时:%d\n", allTime);
        return allTime;
    }

}
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

运行结果:

多运行几次,可以发现数值基本在 26ms ~ 28ms 之间波动

# 三:StringTokenizer

这个工具是 JDK 提供的,也是专门用来进行字符串切割的,如果不用 list 等记录,效果会很好,测试代码如下:

public class StringTokenizerSplitTest {

    public static void main(String[] args) {
        // 获取一个超长字符串
        String string = StringGeneratorUtils.generate();
        List<Long> list = new ArrayList<>(21);

        for (int i = 0; i < 20; i++) {
            System.out.printf("第%d次切割,\t", i);
            list.add(extracted(string));
        }

        System.out.printf("平均耗时:%s", list.stream().mapToLong(Long::longValue).average().getAsDouble());
    }

    private static long extracted(String string) {
        long start = System.currentTimeMillis();
        StringTokenizer stringTokenizer = new StringTokenizer(string, ",");
        List<String> list = new ArrayList<>(10_0001);
        for (int i = 0; i < 100; i++) {
            list.clear();
            // 切割字符串
            while (stringTokenizer.hasMoreTokens()) {
                list.add(stringTokenizer.nextToken());
            }
            stringTokenizer = new StringTokenizer(string, ",");
        }
        System.out.printf("切割耗时:%d", System.currentTimeMillis() - start);
        System.out.printf("查找第一个数字:%s,查找最后一个数字:%s,", list.get(0), list.get(list.size() - 1));
        long allTime = System.currentTimeMillis() - start;
        System.out.printf("总耗时:%d\n", allTime);
        return allTime;
    }

}
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

运行结果:

多运行几次,可以发现数值基本在 27ms ~ 30ms 之间波动,这里面的时间,有一部分消耗在了 list.add() 等操作上

# 四:Apache-commons

下面使用 apache commons工具包,里面的StringUtils工具类,进行切割测试

首先引入相关的依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>
1
2
3
4
5

测试类如下:

import org.apache.commons.lang3.StringUtils;

public class ApacheTest {

    public static void main(String[] args) {
        // 获取一个超长字符串
        String string = StringGeneratorUtils.generate();
        List<Long> list = new ArrayList<>(21);

        for (int i = 1; i < 21; i++) {
            System.out.printf("第%d次切割,\t", i);
            list.add(extracted(string));
        }

        System.out.printf("平均耗时:%s", list.stream().mapToLong(Long::longValue).average().getAsDouble());
    }

    private static long extracted(String string) {
        String[] strings = null;
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            // 切割字符串
            strings = StringUtils.split(string, ",");
        }
        System.out.printf("切割耗时:%d,", System.currentTimeMillis() - start);
        System.out.printf("查找第一个数字:%s,查找最后一个数字:%s,", strings[0], strings[strings.length - 1]);
        long allTime = System.currentTimeMillis() - start;
        System.out.printf("总耗时:%d\n", allTime);
        return allTime;
    }

}
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

运行结果:

多运行几次,可以发现数值基本在 44ms ~ 48ms 之间波动

# 五:小结

其实总的来说,如果在项目中使用,基本单个切割的时间可以忽略不计。所以直接使用 String.split() 即可。如果遇到一边切割,一边直接处理,不用存储的话,可以考虑直接用 StringTokenizer。

最后更新: 9/23/2023, 3:55:03 PM