如何使用Java Stream删除文件夹中的重复文件?
用Stream API删除内容重复的文件
你原来的方法为什么失效?
- 直接用
File流的distinct():File类的equals()方法比较的是文件路径和名称,而非内容。所以即使内容完全相同,只要文件名不同,distinct()会把它们当成不同元素,最终所有文件都会被删除。 - 转字节数组用
distinct():Java数组的equals()是引用比较,不是内容比较。两个内容完全一致的字节数组,只要是不同对象,distinct()就会认为它们不同,自然识别不了重复。
仅用Stream实现的正确方案
核心思路是:用文件内容的哈希值(比如MD5)标识重复,按哈希值分组后,每组只保留一个文件,删除其余。哈希值相同的文件,内容重复的概率几乎为100%(可忽略碰撞情况)。
代码示例:
import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Map; import java.util.stream.Collectors; public class DuplicateFileRemover { public static void main(String[] args) throws IOException, NoSuchAlgorithmException { File directory = new File("D:\\Photos\\Test"); File[] files = directory.listFiles(); if (files == null) return; // 按文件内容的MD5哈希分组,每组保留第一个文件 Map<String, File> uniqueFiles = Files.list(directory.toPath()) .filter(Files::isRegularFile) .collect(Collectors.toMap( path -> getFileHash(path), // 哈希值作为分组key path -> path.toFile(), // 文件对象作为value (existing, replacement) -> existing // 重复时保留第一个文件 )); // 删除不在唯一文件集合里的重复文件 Files.list(directory.toPath()) .filter(Files::isRegularFile) .map(path -> path.toFile()) .filter(file -> !uniqueFiles.containsValue(file)) .forEach(file -> { try { Files.delete(file.toPath()); } catch (IOException e) { e.printStackTrace(); } }); } // 计算文件MD5哈希值,转为Base64字符串方便存储 private static String getFileHash(java.nio.file.Path path) throws IOException, NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] fileBytes = Files.readAllBytes(path); md.update(fileBytes); byte[] hashBytes = md.digest(); return Base64.getEncoder().encodeToString(hashBytes); } }
补充说明
- 如果处理大文件,
readAllBytes()可能占用过多内存,可改用流式读取计算哈希:
private static String getLargeFileHash(java.nio.file.Path path) throws IOException, NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("MD5"); try (var inputStream = Files.newInputStream(path)) { byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { md.update(buffer, 0, bytesRead); } } byte[] hashBytes = md.digest(); return Base64.getEncoder().encodeToString(hashBytes); }
- 若想保留最新/最旧文件,可修改
Collectors.toMap()的第三个参数,比如(existing, replacement) -> replacement会保留最后遍历到的文件,配合Sorted()还能控制遍历顺序。
内容的提问来源于stack exchange,提问作者VegetaSan
相关产品推荐
相关产品推荐

