在JAVA中遍歷目錄下的所有文件,很多人第一個想到的是用File.listFiles方法遞歸. 然而這并不是最好的方法, 這里跟幾個其他的方法做一個對比.
- 自己寫函數(shù)遞歸
- google的guava工具包
- common io工具包
- jdk自帶的nio Files工具類
package test.walkfile;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.concurrent.ConcurrentSkipListSet;
import org.Apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import com.google.common.io.Files;
import util.TimeUtil;
public class WalkFile {
private static final String rootPath = "D:\";
public static void main(String[] args) throws IOException {
Collection<File> res = new ConcurrentSkipListSet<>();
File root = new File(rootPath);
TimeUtil.timeStart("byFunction");
byFunction(root, res);
System.out.println(res.size());
TimeUtil.timeEnd("byFunction");
res.clear();
TimeUtil.timeStart("guavaBreadthFirst");
guavaBreadthFirst(root, res);
System.out.println(res.size());
TimeUtil.timeEnd("guavaBreadthFirst");
res.clear();
TimeUtil.timeStart("guavaDepthFirst");
guavaDepthFirst(root, res);
System.out.println(res.size());
TimeUtil.timeEnd("guavaDepthFirst");
res.clear();
TimeUtil.timeStart("byCommonio");
byCommonio(root, res);
System.out.println(res.size());
TimeUtil.timeEnd("byCommonio");
res.clear();
TimeUtil.timeStart("byJdkFiles");
byJdkFiles(root, res);
System.out.println(res.size());
TimeUtil.timeEnd("byJdkFiles");
res.clear();
}
/**
* 自己寫算法遍歷文件.
*
* @param root
* 根路徑
* @param allFiles
* 遍歷結(jié)果集合
*/
private static void byFunction(File root, Collection<File> allFiles) {
if (root == null || !root.exists()) {
return;
}
if (root.isFile()) {
allFiles.add(root);
} else {
File[] listFiles = root.listFiles();
if (listFiles != null) {
for (File childFile : listFiles) {
byFunction(childFile, allFiles);
}
}
}
}
/**
* guava 廣度優(yōu)先遍歷文件.
*
* @param root
* 根路徑
* @param allFiles
* 遍歷結(jié)果集合
*/
private static void guavaBreadthFirst(File root, Collection<File> allFiles) {
Iterable<File> files = Files.fileTraverser().breadthFirst(root);
files.forEach(f -> allFiles.add(f));
}
/**
* guava 深度優(yōu)先遍歷文件.
*
* @param root
* 根路徑
* @param allFiles
* 遍歷結(jié)果集合
*/
private static void guavaDepthFirst(File root, Collection<File> allFiles) {
Iterable<File> files = Files.fileTraverser().depthFirstPostOrder(root);
files.forEach(f -> allFiles.add(f));
}
/**
* 通過common io工具包遍歷文件.
*
* @param root
* 根路徑
* @param allFiles
* 遍歷結(jié)果集合
*/
private static void byCommonio(File root, Collection<File> allFiles) {
allFiles.addAll(FileUtils.listFiles(root, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE));
}
/**
* 通過jdk的nio Files工具類遍歷文件.
*
* @param root
* 根路徑
* @param allFiles
* 遍歷結(jié)果集合
* @throws IOException
* IO異常
*/
private static void byJdkFiles(File root, Collection<File> allFiles) throws IOException {
java.nio.file.Files.walkFileTree(root.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
allFiles.add(file.toFile());
return super.visitFile(file, attrs);
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
});
}
}
程序執(zhí)行結(jié)果如下:
152058 byFunction:6584 ms 178667 guavaBreadthFirst:5539 ms 178667 guavaDepthFirst:5153 ms 152058 byCommonio:6543 ms 152058 byJdkFiles:2272 ms
執(zhí)行3次結(jié)果統(tǒng)計:
3次執(zhí)行時間
jdk自帶的Files類比自己遞歸節(jié)約了: (6443 - 2483)÷6443 = 61%的時間. 有些情況下甚至可以節(jié)約80%以上的時間. 所以還有什么理由再去自己寫算法呢?
除了遍歷文件,還有復(fù)制文件也是如此. jdk nio的Files工具類用時大概是普通buffer數(shù)組方式的1/3, FileChannel和Files差不多一樣快.
當(dāng)性能想要大幅提高的時候,先去java類庫找有沒有合適的工具類.如果沒有再考慮其他jar包. 另外發(fā)現(xiàn),common的jar包一般性能不是很好, 可能偏重于可靠性, google的工具包要好一些.






