尽可能快地通过java读取数百万行的csv文件

时间:2016-03-31 18:32:15

标签: java csv

我想读取包含数百万行的csv文件,并使用我的决策树算法的属性。我的代码如下:

String csvFile = "myfile.csv";
List<String[]> rowList = new ArrayList();
String line = "";
String cvsSplitBy = ",";
String encoding = "UTF-8";
BufferedReader br2 = null;
try {
    int counterRow = 0;
    br2 =  new BufferedReader(new InputStreamReader(new FileInputStream(csvFile), encoding));
    while ((line = br2.readLine()) != null) { 
        line=line.replaceAll(",,", ",NA,");
        String[] object = line.split(cvsSplitBy);
        rowList.add(object); 
        counterRow++;
    }
    System.out.println("counterRow is: "+counterRow);
    for(int i=1;i<rowList.size();i++){
        try{
           //this method includes many if elses only.
           ImplementDecisionTreeRulesFor2012(rowList.get(i)[0],rowList.get(i)[1],rowList.get(i)[2],rowList.get(i)[3],rowList.get(i)[4],rowList.get(i)[5],rowList.get(i)[6]); 
        }
        catch(Exception ex){
           System.out.printlnt("Exception occurred");   
        }
    }
}
catch(Exception ex){
    System.out.println("fix"+ex);
}

当csv文件的大小不大时工作正常。但是,确实很大。因此我需要另一种方法来更快地读取csv。有什么建议吗?感谢,谢谢。

4 个答案:

答案 0 :(得分:9)

在这个片段中,我看到两个问题会让你大大减慢速度:

while ((line = br2.readLine()) != null) { 
    line=line.replaceAll(",,", ",NA,");
    String[] object = line.split(cvsSplitBy);
    rowList.add(object); 
    counterRow++;
}

首先,rowList以默认容量开始,并且必须多次增加,总是导致旧的底层数组的副本为新的。

然而,更糟糕的是,数据过度爆炸成String []对象。只有在为该行调用ImplementDecisionTreeRulesFor2012 时才需要列/单元格 - 在读取该文件并处理所有其他行时并非所有时间。将分组(或更好的内容,如注释所示)移动到第二行。

(创建很多对象很糟糕,即使你能负担得起内存。)

也许最好在阅读“百万”时调用ImplementDecisionTreeRulesFor2012?它会完全避免使用rowList ArrayList。

<强>后来 推迟拆分减少了1000万行的执行时间 从1m8.262s(当程序用完堆空间时)到13.067s。

如果您在调用Implp ... 2012之前没有被强制阅读所有行,则时间会减少到4.902秒。

最后编写分割并手动替换:

String[] object = new String[7];
//...read...
    String x = line + ",";
    int iPos = 0;
    int iStr = 0; 
    int iNext = -1;
    while( (iNext = x.indexOf( ',', iPos )) != -1 && iStr < 7 ){
        if( iNext == iPos ){
            object[iStr++] = "NA";
        } else {
             object[iStr++] = x.substring( iPos, iNext );
        }
        iPos = iNext + 1;
    }
    // add more "NA" if rows can have less than 7 cells

将时间缩短到1.983秒。这比原始代码快30倍左右,原始代码无论如何都会遇到OutOfMemory。

答案 1 :(得分:5)

只需使用uniVocity-parsers'CSV解析器,而不是尝试构建自定义解析器。您的实施可能不会快速或灵活,无法处理所有极端情况。

它具有极高的内存效率,您可以在不到一秒的时间内解析一百万行。 This link对许多java CSV库进行了性能比较,并且将univocity-parsers放在首位。

以下是如何使用它的简单示例:

CsvParserSettings settings = new CsvParserSettings(); // you'll find many options here, check the tutorial.
CsvParser parser = new CsvParser(settings);

// parses all rows in one go (you should probably use a RowProcessor or iterate row by row if there are many rows)
List<String[]> allRows = parser.parseAll(new File("/path/to/your.csv"));

但是,它将所有内容加载到内存中。要流式传输所有行,您可以执行以下操作:

String[] row;
parser.beginParsing(csvFile)
while ((row = parser.parseNext()) != null) {
    //process row here.
}

更快的方法是使用RowProcessor,它还提供了更大的灵活性:

settings.setRowProcessor(myChosenRowProcessor);
CsvParser parser = new CsvParser(settings);
parser.parse(csvFile);

最后,它内置routines使用解析器执行一些常见任务(迭代java bean,转储ResultSet等)

这应涵盖基础知识,查看文档以找到适合您案例的最佳方法。

披露:我是这个图书馆的作者。它是开源和免费的(Apache V2.0许可证)。

答案 2 :(得分:1)

除了上述的不公平之外,还值得检查

其中3个将作为评论时间最快的csv解析器。

很有可能写你自己的解析器会变慢而且错误。

答案 3 :(得分:0)

如果您的目标是对象(即数据绑定),那么我已经编写了一个高性能的sesseltjonna-csv库,您可能会觉得很有趣。与SimpleFlatMapper和uniVocity here进行基准比较。