如何使用Java多线程概念将多个csv文件合并到一个文件中

时间:2017-07-11 14:11:15

标签: java multithreading

我想将某个文件夹中的csv文件合并到一个文件中。假设文件夹中有12000个文件,每个文件有20000个记录。任何人都可以使用多线程概念给我一些最好的想法/解决方案。 我已经写了下面的代码,但我认为对于大数据它并不好:

public class Test {
    public static void main(String[] args) throws IOException {

        String path="Desktop//Files//";
        List<Path> listofFile=getListOfFileInFolder(path);  
        List<Path> paths = listofFile;
        List<String> mergedLines = getMergedLines(paths);
        Path target = Paths.get(path+"temp.csv");
        System.out.println(target);
        Files.write(target, mergedLines, Charset.forName("UTF-8")); }

    public static List<Path> getListOfFileInFolder(String path){
    List<Path> results = new ArrayList<Path>();
    File[] files = new File(path).listFiles();
    for (File file : files) {
        if (file.isFile()) {
         results.add(Paths.get(path+file.getName()));
        }
    }
    return results;
    }
    private static List<String> getMergedLines(List<Path> paths) throws IOException {
        List<String> mergedLines = new ArrayList<> ();
        for (Path p : paths){
            List<String> lines = Files.readAllLines(p, Charset.forName("UTF-8"));
            if (!lines.isEmpty()) {
                if (mergedLines.isEmpty()) {
                    mergedLines.add(lines.get(0)); //add header only once
                }
                mergedLines.addAll(lines.subList(1, lines.size()));
                }
        }
        return mergedLines;
    }
}

1 个答案:

答案 0 :(得分:1)

在这种情况下,多线程不太可能提高性能。通过使用多个核心,多线程可以在CPU成为瓶颈时加快批量操作。但在您的过程中,瓶颈将是磁盘读取。单个CPU核心将在文件系统传递字节的同时快速处理合并。

您提出的文件数量最大的问题是初始目录listFiles()需要一些时间,结果File[20000]会消耗大量内存。

同样地,对于10,000个记录文件,使用readAllLines()将整个内容啜饮到内存中会占用大量内存,因此没有充分理由使用GC。

并且,您将结果收集到String列表中,当它有20,000,000 * 10,000行时,将会有200,000,000个条目。对于80列文件,这是16GB的字符串,加上对象开销。

最好一次读取少量内容,将其写入输出文件,然后尽早从内存中删除。

你可以通过事件驱动,并使用java.nio.Files.walkFileTree()

来做到这一点
 try(OutputStream out = new FileOutputStream(outpath)) {
     Files.walkFileTree(inputDirectoryPath, Collections.emptySet(), 1, new MergeToOutputStreamVisitor(out));
 }

...其中MergeToOutputStreamVisitor类似于:

 public class MergeToOutputStreamVisitor extends SimpleFileVisitor {

     private final OutputStream outstream;

     @Override
     public FileVisitResult visitFile(T file, BasicFileAttributes attrs) {
         FileUtils.copyFile(file, outstream); 
         return FileVisitResult.CONTINUE;
     }
 }

我已经使用Apache Commons-IO FileUtils.copyFile()将每个文件的内容喷入OutputStream。如果您不能使用Commons-IO,请编写您自己的版本。如果您需要做更多事情,例如,跳过第一行,您也可以使用以下内容滚动自己:

   try(BufferedReader reader = new BufferedReader(new FileReader(file)); Writer writer = new OutputStreamWriter(outstream)) {
       reader.readLine(); // header - throw it away
       String line = reader.readLine();
       while(line != null) {
           writer.write(line);
           line = reader.readLine();
       }
   }

使用这种方法,您的程序在任何时候最多只有一个File条目和一行内存中的一行数据(加上库例程使用的任何缓冲 - 这很好)。它将直接从输入文件流到行输出文件。我保证你不会100%使用单核,因此多线程不会让它更快。