我想将某个文件夹中的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;
}
}
答案 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%使用单核,因此多线程不会让它更快。