带有大文件的Java CRC(Adler)
我有以下情况:一个包含大文件的目录树(大约5000个~4Gb大小的文件)。我需要在这棵树上找到重复的 我尝试使用Java内置的CRC32和Adler32类,但速度非常慢(每个文件大约3-4分钟) 代码是这样的:带有大文件的Java CRC(Adler),java,checksum,large-files,Java,Checksum,Large Files,我有以下情况:一个包含大文件的目录树(大约5000个~4Gb大小的文件)。我需要在这棵树上找到重复的 我尝试使用Java内置的CRC32和Adler32类,但速度非常慢(每个文件大约3-4分钟) 代码是这样的: 1) Init map <path, checksum> 2) Create CheckSum instance (CRC32 or Adler32); 3) Read file per block (10-100 bytes); 4) In each iteration c
1) Init map <path, checksum>
2) Create CheckSum instance (CRC32 or Adler32);
3) Read file per block (10-100 bytes);
4) In each iteration call update()
5) Result checksum passed in map <path, summ>
6) Find duplicates
1)初始化映射
2) 创建校验和实例(CRC32或Adler32);
3) 每个块读取文件(10-100字节);
4) 在每次迭代调用update()中
5) 映射中传递的结果校验和
6) 查找重复项
问题:有没有办法加快第3-4行中校验和的收集速度?我将以以下方式处理此问题:
Map
而不是Map
。
通过这种方式,您可以直接删除所有列表大小为1的条目,而无需进一步查找或比较
也许还有比这更聪明的方法,当我读到这个任务的时候,我就把脑子里想的都抛在脑后
我已经草拟了一个小程序,几乎可以执行这一点。它不获取片段中的校验和。这是因为当我在236670个文件上运行这个程序时,总共有6GB的数据,只花了7秒钟。免责声明:我有一个SSD。但也许我会更新部分校验和的程序
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.*;
import java.util.zip.*;
public class FindDuplicates {
public static void main(final String... args) throws IOException {
findDuplicates(argsOrCurrentDirectory(args));
}
private static String[] argsOrCurrentDirectory(final String... args) {
return args.length == 0 ? new String[] {"."} : args;
}
private static void findDuplicates(final String... paths) throws IOException {
final Stream<Path> allFilesInPaths = find(paths);
final Map<Long, List<Path>> filesBySize = allFilesInPaths.collect(Collectors.groupingByConcurrent(path -> path.toFile().length()));
final Stream<Path> filesWithNonUniqueSizes = getValueStreamFromDuplicates(filesBySize);
final Map<Long, List<Path>> filesByChecksum = filesWithNonUniqueSizes.collect(Collectors.groupingBy(FindDuplicates::getChecksum));
final Stream<Path> filesWithNonUniqueChecksums = getValueStreamFromDuplicates(filesByChecksum);
filesWithNonUniqueChecksums.forEach(System.out::println);
}
private static Stream<Path> toPaths(final String... pathnames) {
return Arrays.asList(pathnames).parallelStream().map(FileSystems.getDefault()::getPath);
}
private static Stream<Path> find(final String... pathnames) {
return find(toPaths(pathnames));
}
private static Stream<Path> find(final Stream<Path> paths) {
return paths.flatMap(FindDuplicates::findSinglePath);
}
private static Stream<Path> findSinglePath(final Path path) {
try {
return Files.find(path, 127, ($, attrs) -> attrs.isRegularFile());
} catch (final IOException e) {
System.err.format("%s: error: Unable to traverse path: %s%n", path, e.getMessage());
return Stream.empty();
}
}
public static <V> Stream<V> getValueStreamFromDuplicates(final Map<?, List<V>> original) {
return original.values().parallelStream().filter(list -> list.size() > 1).flatMap(Collection::parallelStream);
}
public static long getChecksum(final Path path) {
try (final CheckedInputStream in = new CheckedInputStream(new BufferedInputStream(new FileInputStream(path.toFile())), new CRC32())) {
return tryGetChecksum(in);
} catch (final IOException e) {
System.err.format("%s: error: Unable to calculate checksum: %s%n", path, e.getMessage());
return 0L;
}
}
public static long tryGetChecksum(final CheckedInputStream in) throws IOException {
final byte[] buf = new byte[4096];
for (int bytesRead; (bytesRead = in.read(buf)) != -1; );
return in.getChecksum().getValue();
}
}
import java.io.*;
导入java.nio.file.*;
导入java.nio.file.attribute.*;
导入java.util.*;
导入java.util.concurrent.*;
导入java.util.function.*;
导入java.util.stream.*;
导入java.util.zip.*;
公共类查找副本{
公共静态void main(最终字符串…args)引发IOException{
findDuplicates(argsOrCurrentDirectory(args));
}
私有静态字符串[]argsOrCurrentDirectory(最终字符串…args){
返回args.length==0?新字符串[]{“.”}:args;
}
私有静态void findDuplicates(最终字符串…路径)引发IOException{
最终流allFilesInPaths=find(路径);
final-Map-filesBySize=allFilesInPaths.collect(Collectors.groupingByConcurrent(path->path.toFile().length());
最终流fileswithununiquesizes=getValueStreamFromDuplicates(filesBySize);
final-Map filesByChecksum=fileswithununiquesizes.collect(Collectors.groupingBy(FindDuplicates::getChecksum));
最终流fileswithununiquechecksums=getValueStreamFromDuplicates(filesByChecksum);
fileswithununiquechecksums.forEach(System.out::println);
}
私有静态流toPaths(最终字符串…路径名){
返回Arrays.asList(路径名).parallelStream().map(FileSystems.getDefault()::getPath);
}
私有静态流查找(最终字符串…路径名){
返回find(toPaths(路径名));
}
专用静态流查找(最终流路径){
返回路径.flatMap(FindDuplicates::findSinglePath);
}
私有静态流findSinglePath(最终路径){
试一试{
返回Files.find(路径127,($,attrs)->attrs.isRegularFile());
}捕获(最终IOE例外){
System.err.format(“%s:error:无法遍历路径:%s%n”,路径,例如getMessage());
返回Stream.empty();
}
}
公共静态流getValueStreamFromDuplicates(final Map我将以以下方式处理此问题:
试着使用Java8并行流或类似的东西,这样我的多核CPU就可以用来加快速度
用所有文件填充地图
获取所有文件大小
删除所有具有唯一文件大小的文件。如果您的文件“非常正常”,则很可能已经删除了多个较大的文件。实际上,可以通过将文件大小用作映射中的键,将列表用作值来完成此操作。然后删除列表大小为1的所有条目
获取每个文件的下一个(首次:首次)4K字节的校验和
消除所有具有唯一校验和的文件
重复4.和5.直到没有唯一校验和的文件都被完全读取
其余文件是重复的文件(如果校验和没有冲突)
比较重复文件的文件内容以防冲突
加速校验和的关键是分块进行校验和比较。如果第一个字节已经不同,为什么还要看其余的字节呢
加速比较的另一个关键可能是反转地图。我会使用Map
而不是Map
。
通过这种方式,您可以直接删除所有列表大小为1的条目,而无需进一步查找或比较
也许还有比这更聪明的方法,当我读到这个任务的时候,我就把脑子里想的都抛在脑后
我已经草拟了一个小程序,几乎可以执行这项工作。它不会在片段中获取校验和。原因是