StringTokenizer - 用整数读取行

时间:2013-10-14 08:17:52

标签: java stringtokenizer

我有一个关于优化我的代码的问题(这可行,但速度太慢......)。我正在阅读表格中的输入

X1 Y1
X2 Y2
etc
其中Xi,Yi是整数。我使用bufferedReader来阅读行,然后使用StringTokenizer来处理这些数字:

StringTokenizer st = new StringTokenizer(line, " ");

int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());

问题在于,在处理大型数据集时,这种方法似乎效率低下。你能给我一些简单的改进(我听说可以使用一些整数解析int或正则表达式),这会改善性能吗?感谢您的任何提示

编辑:也许我误判了自己,并且必须在代码的其他地方进行一些改进......

2 个答案:

答案 0 :(得分:2)

(更新回答)

我可以说,无论程序速度有什么问题,标记器的选择都不是其中之一。在每个方法初始运行以均衡初始化怪癖之后,我可以在几毫秒内解析1000000行“12 34”。如果你愿意,你可以切换到使用indexOf,但我真的认为你需要查看其他代码的瓶颈而不是这个微优化。斯普利特对我来说是一个惊喜 - 与其他方法相比,它真的非常慢。我已经添加了Guava split test,它比String.split快,但比StringTokenizer稍慢。

  • 分裂:371ms
  • IndexOf:48ms
  • StringTokenizer:92ms
  • Guava Splitter.split():108ms
  • CsvMapper构建一个csv doc并解析为POJOS:237ms(如果你将这些行构建成一个doc,则为175!)

即使数百万行,这里的差异也几乎可以忽略不计。

现在我的博客上写了这篇文章:http://demeranville.com/battle-of-the-tokenizers-delimited-text-parser-performance/

我跑的代码是:

import java.util.StringTokenizer;
import org.junit.Test;

public class TestSplitter {

private static final String line = "12 34";
private static final int RUNS = 1000000;//000000;

public final void testSplit() {
    long start = System.currentTimeMillis();
    for (int i=0;i<RUNS;i++){
        String[] st = line.split(" ");
        int x = Integer.parseInt(st[0]);
        int y = Integer.parseInt(st[1]);
    }
    System.out.println("Split: "+(System.currentTimeMillis() - start)+"ms");
}

public final void testIndexOf() {
    long start = System.currentTimeMillis();
    for (int i=0;i<RUNS;i++){
        int index = line.indexOf(' ');
        int x = Integer.parseInt(line.substring(0,index));
        int y = Integer.parseInt(line.substring(index+1));
    }       
    System.out.println("IndexOf: "+(System.currentTimeMillis() - start)+"ms");      
}

public final void testTokenizer() {
    long start = System.currentTimeMillis();
    for (int i=0;i<RUNS;i++){
        StringTokenizer st = new StringTokenizer(line, " ");
        int x = Integer.parseInt(st.nextToken());
        int y = Integer.parseInt(st.nextToken());
    }
    System.out.println("StringTokenizer: "+(System.currentTimeMillis() - start)+"ms");
}

@Test
public final void testAll() {
    this.testSplit();
    this.testIndexOf();
    this.testTokenizer();
    this.testSplit();
    this.testIndexOf();
    this.testTokenizer();
}

}

eta:这是番石榴代码:

public final void testGuavaSplit() {
    long start = System.currentTimeMillis();
    Splitter split = Splitter.on(" ");
    for (int i=0;i<RUNS;i++){
        Iterator<String> it = split.split(line).iterator();
        int x = Integer.parseInt(it.next());
        int y = Integer.parseInt(it.next());
    }
    System.out.println("GuavaSplit: "+(System.currentTimeMillis() - start)+"ms");
}

<强>更新

我也在CsvMapper测试中添加了:

public static class CSV{
    public int x;
    public int y;
}

public final void testJacksonSplit() throws JsonProcessingException, IOException {
    CsvMapper mapper = new CsvMapper();
    CsvSchema schema = CsvSchema.builder().addColumn("x", ColumnType.NUMBER).addColumn("y", ColumnType.NUMBER).setColumnSeparator(' ').build();

    long start = System.currentTimeMillis();
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < RUNS; i++) {
        builder.append(line);
        builder.append('\n');
    }       
    String input = builder.toString();
    MappingIterator<CSV> it = mapper.reader(CSV.class).with(schema).readValues(input);
    while (it.hasNext()){
        CSV csv = it.next();
    }
    System.out.println("CsvMapperSplit: " + (System.currentTimeMillis() - start) + "ms");
}

答案 1 :(得分:0)

您可以使用正则表达式检查值是否为数字,然后转换为整数:

if(st.nextToken().matches("^[0-9]+$"))
        {
           int x = Integer.parseInt(st.nextToken());
        }