Java中StringTokenizer类与String.split方法的性能比较

Java中StringTokenizer类与String.split方法的性能比较,java,performance,stringtokenizer,Java,Performance,Stringtokenizer,在我的软件中,我需要将字符串拆分为单词。我目前有超过19000000份文件,每个文件超过30个字 以下哪两种方法是最好的方法(就性能而言) 或 String[]splitS=s.split(“”); 对于(int i=0;i

在我的软件中,我需要将字符串拆分为单词。我目前有超过19000000份文件,每个文件超过30个字

以下哪两种方法是最好的方法(就性能而言)

String[]splitS=s.split(“”);
对于(int i=0;i
使用拆分


StringTokenizer是一个遗留类,尽管新代码中不鼓励使用它,但出于兼容性原因保留它。建议寻求此功能的用户改用split方法。

如果数据库中已有数据,您需要解析字符串,我建议重复使用indexOf。它比任何一种解决方案都快很多倍

然而,从数据库中获取数据的成本可能要高得多

StringBuilder sb = new StringBuilder();
for (int i = 100000; i < 100000 + 60; i++)
    sb.append(i).append(' ');
String sample = sb.toString();

int runs = 100000;
for (int i = 0; i < 5; i++) {
    {
        long start = System.nanoTime();
        for (int r = 0; r < runs; r++) {
            StringTokenizer st = new StringTokenizer(sample);
            List<String> list = new ArrayList<String>();
            while (st.hasMoreTokens())
                list.add(st.nextToken());
        }
        long time = System.nanoTime() - start;
        System.out.printf("StringTokenizer took an average of %.1f us%n", time / runs / 1000.0);
    }
    {
        long start = System.nanoTime();
        Pattern spacePattern = Pattern.compile(" ");
        for (int r = 0; r < runs; r++) {
            List<String> list = Arrays.asList(spacePattern.split(sample, 0));
        }
        long time = System.nanoTime() - start;
        System.out.printf("Pattern.split took an average of %.1f us%n", time / runs / 1000.0);
    }
    {
        long start = System.nanoTime();
        for (int r = 0; r < runs; r++) {
            List<String> list = new ArrayList<String>();
            int pos = 0, end;
            while ((end = sample.indexOf(' ', pos)) >= 0) {
                list.add(sample.substring(pos, end));
                pos = end + 1;
            }
        }
        long time = System.nanoTime() - start;
        System.out.printf("indexOf loop took an average of %.1f us%n", time / runs / 1000.0);
    }
 }

打开文件的成本约为8毫秒。由于文件太小,您的缓存可能会将性能提高2-5倍。即便如此,它也要花10个小时打开文件。使用split vs StringTokenizer的成本远低于0.01毫秒。解析1900万x 30个单词*每个单词8个字母大约需要10秒(大约每2秒1 GB)


如果您想提高性能,我建议您的文件要少得多。e、 g.使用数据库。如果您不想使用SQL数据库,我建议您使用其中的一个,19000000个文档在这里要做什么?你必须定期拆分所有文档中的单词吗?或者这是一个一次性的问题

如果一次只显示/请求一个文档,只需30个单词,这是一个非常小的问题,任何方法都可以解决


如果一次只需处理30个字的所有文档,那么这是一个非常小的问题,您很可能会被IO绑定。

Java API规范建议使用
split
。请参阅。

无论其遗留状态如何,我希望
StringTokenizer
对于此任务要比
String.split()
快得多,因为它不使用正则表达式:它只是直接扫描输入,就像您自己通过
indexOf()
进行扫描一样。事实上,
String.split()
必须在每次调用正则表达式时编译它,因此它甚至不如您自己直接使用正则表达式那么有效。

Java 7中的split只为该输入调用indexOf。Split应该非常快,接近重复调用indexOf。

另一件重要的事情,据我所知,没有记录在案,就是要求StringTokenizer返回分隔符和标记化字符串(通过使用构造函数
StringTokenizer(string str,string delim,boolean returnDelims)
)也减少了处理时间。因此,如果您希望获得性能,我建议您使用以下方法:

private static final String DELIM = "#";

public void splitIt(String input) {
    StringTokenizer st = new StringTokenizer(input, DELIM, true);
    while (st.hasMoreTokens()) {
        String next = getNext(st);
        System.out.println(next);
    }
}

private String getNext(StringTokenizer st){  
    String value = st.nextToken();
    if (DELIM.equals(value))  
        value = null;  
    else if (st.hasMoreTokens())  
        st.nextToken();  
    return value;  
}
尽管getNext()方法引入了开销,为您丢弃了分隔符,但根据我的基准测试,它仍然快了50%。

在运行micro(在本例中,甚至是nano)基准测试时,有很多因素会影响您的结果。JIT优化和垃圾收集仅举几个例子


为了从微基准测试中获得有意义的结果,请查看库。它提供了关于如何运行良好基准测试的优秀示例。

这可能是使用1.6.0进行的合理基准测试

http://www.javamex.com/tutorials/regular_expressions/splitting_tokenisation_performance.shtml#.V6-CZvnhCM8

性能方面的StringTokeniser比split要好得多。检查下面的代码



但是根据Java文档,它的使用是不被鼓励的。检查

我会先计算,但你为什么不测量一下呢?我可以,但我也对解释感兴趣……如果有人说选项X是最快的呢?你会选择这个选项吗,或者只是为了确定,你会同时测试这两个选项吗?如果是后者,为什么不马上做呢?:)@约翰:请清楚地回答你的问题,你是否需要在标记化和拆分之间找到更好的方法,或者你正在寻找最好的方法来减少标记化和拆分split@Damodar“就表现而言,这是最好的办法”@downvorters:请澄清上述问题,您是否需要在Tokenize与split之间找到更好的方法,或者您正在寻找最好的方法,而不是Tokenize与split。问题很明显,他正在寻找在性能方面做到这一点的最佳方法。API建议使用split,但没有提到(根据我通过Google找到的所有其他信息)Tokenize性能更好。@Bill,很抱歉我犯了一个错误。那么他们可能会改变问题的标题为什么是-1?这确实正确地回答了是否使用split或StringTokenizer的问题。该规范确实提到建议拆分而不是StringTokenizer请参见我对Damodar答案的评论。规范没有提到任何关于性能的问题,这正是这个问题所要问的。谢谢比尔rationalSpringI投了反对票,因为它不询问是否使用其中一个,而是询问哪一个更快。有趣的是,我运行了你的代码,
split
在我的机器上花费的时间是
StringTokenizer
的两倍
indexof
需要一半的时间。扫描器和字符串标记器使用Pattern/regex,这比查找特定字符更灵活,但效率不如。@Peter Lawrey:StringTokenizer不使用regex。@tjjjohnson Java 7 split执行类似于indexof上的一系列操作,但仅限于,但是非常常见的操作。只是为了记录,indexOf循环的实现是不正确的,缺少最后一个分隔符之后的部分。不确定这对性能有多大影响,但无论如何,你确定吗?我可以在您提供的链接下的第2361行看到:
return Pattern.compile(regex).split(this,limit)实现是在1770年。如果正则表达式满足某些标准,则实现将使用(即
indexOf
),并将使用
Pattern.compile(正则表达式).split(此,限制)否则。从源代码:
f
StringTokenizer took an average of 5.8 us
Pattern.split took an average of 4.8 us
indexOf loop took an average of 1.8 us
StringTokenizer took an average of 4.9 us
Pattern.split took an average of 3.7 us
indexOf loop took an average of 1.7 us
StringTokenizer took an average of 5.2 us
Pattern.split took an average of 3.9 us
indexOf loop took an average of 1.8 us
StringTokenizer took an average of 5.1 us
Pattern.split took an average of 4.1 us
indexOf loop took an average of 1.6 us
StringTokenizer took an average of 5.0 us
Pattern.split took an average of 3.8 us
indexOf loop took an average of 1.6 us
private static final String DELIM = "#";

public void splitIt(String input) {
    StringTokenizer st = new StringTokenizer(input, DELIM, true);
    while (st.hasMoreTokens()) {
        String next = getNext(st);
        System.out.println(next);
    }
}

private String getNext(StringTokenizer st){  
    String value = st.nextToken();
    if (DELIM.equals(value))  
        value = null;  
    else if (st.hasMoreTokens())  
        st.nextToken();  
    return value;  
}
http://www.javamex.com/tutorials/regular_expressions/splitting_tokenisation_performance.shtml#.V6-CZvnhCM8