目前我的任务是制作一个可以使用java检查链接是否正确的工具.该链接来自Jericho HTML Parser,我的工作只是检查文件是否存在/链接是否正确.那部分已经完成,困难的部分是优化它,因为我的代码运行(我不得不说)相当缓慢,每次运行65ms
public static String checkRelativeURL(String originalFileLoc, String relativeLoc){
StringBuilder sb = new StringBuilder();
String absolute = Common.relativeToAbsolute(originalFileLoc, relativeLoc); //built in function to replace the link from relative link to absolute path
sb.append(absolute);
sb.append("\t");
try {
Path path = Paths.get(absolute);
sb.append(Files.exists(path));
}catch (InvalidPathException | NullPointerException ex) {
sb.append(false);
}
sb.append("\t");
return sb.toString();
}
Run Code Online (Sandbox Code Playgroud)
在这条线上花了65毫秒
Path path = Paths.get(absolute);
sb.append(Files.exists(path));
Run Code Online (Sandbox Code Playgroud)
我试过用
File file = new File(absolute);
sb.append(file.isFile());
Run Code Online (Sandbox Code Playgroud)
它仍然在65~100ms左右运行.
那么有没有其他更快的方法来检查文件是否存在除此之外?
由于我正在处理超过70k的html文件,每毫秒计数,谢谢:(
编辑:
我尝试将所有文件列入一些List,但它并没有真正帮助,因为它只需要超过20分钟就可以列出所有文件....
我用来列出所有文件的代码
static public void listFiles2(String filepath){
Path path = Paths.get(filepath);
File file = null;
String pathString = new String();
try {
if(path.toFile().isDirectory()){
DirectoryStream<Path> stream = Files.newDirectoryStream(path);
for(Path entry : stream){
file = entry.toFile();
pathString = entry.toString();
if(file.isDirectory()){
listFiles2(pathString);
}
if (file.isFile()){
filesInProject.add(pathString);
System.out.println(pathString);
}
}
stream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Run Code Online (Sandbox Code Playgroud)
如果您提前知道目标操作系统集(通常是这种情况),最终最快的方法将是通过 shell 调用进程(例如使用Runtime.exec )列出如此多的文件。
在 Windows 上你可以这样做
dir /s /b
Run Code Online (Sandbox Code Playgroud)
在 Linux 上
ls -R -1
Run Code Online (Sandbox Code Playgroud)
您可以检查操作系统是什么并使用适当的命令(错误或如果不支持则诉诸目录流)。
如果您希望简单并且不需要报告进度,您可以避免处理进程 IO 并将列表存储到临时文件中,例如ls -R -1 > /tmp/filelist.txt. 或者,您可以直接从过程输出中读取。使用缓冲流、读取器或类似设备进行读取,并具有足够大的缓冲区。
在 SSD 上,它会在一眨眼的时间内完成,而在现代 HDD 上则只需几秒钟(使用这种方法,五十万个文件不是问题)。
获得列表后,您可以根据最大文件数和内存要求以不同的方式处理它。如果要求不严格,例如桌面程序,您可以使用非常简单的代码,例如将完整的文件列表预先加载到 HashSet 中,并在需要时检查是否存在。通过删除公共根来缩短路径将需要更少的内存。您还可以通过仅保留文件名哈希而不是全名来减少内存(公共根删除可能会减少更多)。
或者,如果您愿意,您可以进一步优化它,现在问题只是简化为检查存储在内存或文件中的字符串列表中的字符串是否存在的问题,这有许多众所周知的最佳解决方案。
Bellow 是一个非常宽松、简单的 Windows 示例。它在 HDD(不是 SSD)驱动器根目录上执行大约 400K 文件的 dir,读取字符串集和 md5 集方法的列表和基准(好吧,有点)时间和内存:
public static void main(String args[]) throws Exception {
final Runtime rt = Runtime.getRuntime();
System.out.println("mem " + (rt.totalMemory() - rt.freeMemory())
/ (1024 * 1024) + " Mb");
long time = System.currentTimeMillis();
// windows command: cd to t:\ and run recursive dir
Process p = rt.exec("cmd /c \"t: & dir /s /b > filelist.txt\"");
if (p.waitFor() != 0)
throw new Exception("command has failed");
System.out.println("done executing shell, took "
+ (System.currentTimeMillis() - time) + "ms");
System.out.println();
File f = new File("T:/filelist.txt");
// load into hash set
time = System.currentTimeMillis();
Set<String> fileNames = new HashSet<String>(500000);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(f), StandardCharsets.UTF_8),
50 * 1024 * 1024)) {
for (String line = reader.readLine(); line != null; line = reader
.readLine()) {
fileNames.add(line);
}
}
System.out.println(fileNames.size() + " file names loaded took "
+ (System.currentTimeMillis() - time) + "ms");
System.gc();
System.out.println("mem " + (rt.totalMemory() - rt.freeMemory())
/ (1024 * 1024) + " Mb");
time = System.currentTimeMillis();
// check files
for (int i = 0; i < 70_000; i++) {
StringBuilder fileToCheck = new StringBuilder();
while (fileToCheck.length() < 256)
fileToCheck.append(Double.toString(Math.random()));
if (fileNames.contains(fileToCheck))
System.out.println("to prevent optimization, never executes");
}
System.out.println();
System.out.println("hash set 70K checks took "
+ (System.currentTimeMillis() - time) + "ms");
System.gc();
System.out.println("mem " + (rt.totalMemory() - rt.freeMemory())
/ (1024 * 1024) + " Mb");
// Test memory/performance with MD5 hash set approach instead of full
// names
time = System.currentTimeMillis();
Set<String> nameHashes = new HashSet<String>(50000);
MessageDigest md5 = MessageDigest.getInstance("MD5");
for (String name : fileNames) {
String nameMd5 = new String(md5.digest(name
.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
nameHashes.add(nameMd5);
}
System.out.println();
System.out.println(fileNames.size() + " md5 hashes created, took "
+ (System.currentTimeMillis() - time) + "ms");
fileNames.clear();
fileNames = null;
System.gc();
Thread.sleep(100);
System.gc();
System.out.println("mem " + (rt.totalMemory() - rt.freeMemory())
/ (1024 * 1024) + " Mb");
time = System.currentTimeMillis();
// check files
for (int i = 0; i < 70_000; i++) {
StringBuilder fileToCheck = new StringBuilder();
while (fileToCheck.length() < 256)
fileToCheck.append(Double.toString(Math.random()));
String md5ToCheck = new String(md5.digest(fileToCheck.toString()
.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
if (nameHashes.contains(md5ToCheck))
System.out.println("to prevent optimization, never executes");
}
System.out.println("md5 hash set 70K checks took "
+ (System.currentTimeMillis() - time) + "ms");
System.gc();
System.out.println("mem " + (rt.totalMemory() - rt.freeMemory())
/ (1024 * 1024) + " Mb");
}
Run Code Online (Sandbox Code Playgroud)
输出:
mem 3 Mb
done executing shell, took 5686ms
403108 file names loaded took 382ms
mem 117 Mb
hash set 70K checks took 283ms
mem 117 Mb
403108 md5 hashes created, took 486ms
mem 52 Mb
md5 hash set 70K checks took 366ms
mem 48 Mb
Run Code Online (Sandbox Code Playgroud)