如何有效地输出具有相同内容的文件列表?
Posted
技术标签:
【中文标题】如何有效地输出具有相同内容的文件列表?【英文标题】:How to output the list of files with same content efficiently? 【发布时间】:2017-10-29 07:33:32 【问题描述】:我得到了一个大小相同的文件列表,我需要返回一个列表列表,其中包含所有具有相同内容的文件。
我的想法是首先将文件散列到一个映射中,其键是 md5 散列值,值是具有键散列值的路径列表。这是hashing()
函数的代码:
public static Map<String, List<String>> hashing(List<File> list) throws Exception
Map<String, List<String>> map = new HashMap<>();
for (File f : list)
String path = f.getAbsolutePath();
FileInputStream in = new FileInputStream(path);
byte[] dataBytes = new byte[1024];
MessageDigest md = MessageDigest.getInstance("MD5");
int n = 0;
while ((n = in.read(dataBytes)) != -1)
md.update(dataBytes, 0, n);
byte[] mdBytes = md.digest();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mdBytes.length; i++)
sb.append(Integer.toString((mdBytes[i] & 0xff) + 0x100, 16).substring(1));
String hash = sb.toString();
if (!map.containsKey(hash))
map.put(hash, new ArrayList<>());
map.get(hash).add(path);
return map;
由于两个文件可以散列到相同的值但不同,我想比较具有相同散列值的文件以验证它们是否真的相同。这是checkSame()
函数:
输入List<String>
是具有相同哈希值的文件路径列表,List<List<String>>
是包含具有相同内容的所有文件的列表列表。
public static void checkSame(List<String> list, List<List<String>> result) throws Exception
List<String> temp = new ArrayList<>();
for (int i = 1; i < list.size(); i++)
if (checkContent(list.get(0), list.get(i)))
continue;
list.remove(list.get(i));
temp.add(list.get(i));
if (list.size() > 1)
result.add(list);
if (temp.size() > 1)
checkSame(temp, result);
public static boolean checkContent (String path1, String path2) throws Exception
FileInputStream fis1 = new FileInputStream(path1);
FileInputStream fis2 = new FileInputStream(path2);
BufferedReader input1 = new BufferedReader(new InputStreamReader(fis1));
BufferedReader input2 = new BufferedReader(new InputStreamReader(fis2));
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
String line1, line2;
try
while ((line1 = input1.readLine()) != null && (line2 = input2.readLine()) != null)
sb1.append(line1);
sb2.append(line2);
if (!sb1.toString().equals(sb2.toString()))
return false;
catch(Exception e)
e.printStackTrace();
finally
if (fis1 != null)
fis1.close();
if (fis2 != null)
fis2.close();
return true;
我的问题是:
-
我上面的代码有问题吗?
还有其他更有效的方法来解决这个问题吗?
【问题讨论】:
你应该关闭文件流 听起来像是codereview.stackexchange.com的问题 【参考方案1】:checkSame
不使用 hashing
通过比较 c1 != c2
,您可以比较每次迭代读取的字节数 - 这是没有意义的
checkSame
仅将列表中的第一个文件与所有其他文件进行比较,但不会将秒数与所有其他文件和第三个文件进行比较,等等。
我会稍微修改一下您的方法:我将通过两个参数进行比较:哈希和文件大小。我将创建一个名为MyFile
的新类,它将包含三个字段:字符串名称、字符串哈希码、长尺寸。
然后我会迭代文件并使用您在hashing
中介绍的逻辑来创建MyFile
对象列表。大小可以轻松实现:
private static long fileSize(String filename)
File file = new File(filename);
return file.length();
现在让MyFile
覆盖两个方法:hashCode()
(它将根据您在上一步中计算的哈希码返回一个整数)和equals
,它将仅检查哈希码和大小。例如:
class MyFile
String name;
String hash;
long size;
public MyFile(String name, String hash, long size)
this.name = name;
this.hash = hash;
this.size = size;
@Override
public boolean equals(Object other)
if (!(other instanceof MyFile))
return false;
MyFile o = (MyFile)other;
return o.hash.equals(this.hash) && o.size == this.size;
@Override
public int hashCode()
return hash.hashCode();
现在您可以轻松比较文件并查看它们是否具有相似的内容(即使它们具有不同的路径)。
【讨论】:
感谢您的回答!我已按照您的建议重写了我的代码。但是我仍然对“如果两个文件具有相同的哈希值-其中一个文件将覆盖另一个文件(您最终在地图中只有一个文件)感到困惑。”,这是什么意思?你能解释一下吗?谢谢! checkSame() 仅将列表中的第一个文件与所有其他文件进行比较,但不会将秒数与所有其他文件和第三个文件进行比较,等等。---正如你所说,碰撞是长镜头,所以我只需要删除与他人不一样的文件。我的想法是将第一个文件作为基准,如果其余文件与第一个文件不同,则将其余文件添加到新列表中,然后递归解决。这个方法有问题吗? 关于 MAP:地图中不能有两个不同的项目具有相同的键,所以如果“file1”和“file2”都具有相同的哈希码:“key”你将添加第一个,您的 MAP 将如下所示:"key": "file1"
,当您添加第二个时,它将覆盖第一个,您最终会得到:"key": "file2"
@YuqiShi 至于你的其他问题 - 这种方法很好,只是你可以通过使用 java8 流和groupby
更轻松地做到这一点 - 查一下!
但是 Map以上是关于如何有效地输出具有相同内容的文件列表?的主要内容,如果未能解决你的问题,请参考以下文章
给定一个包含几千个文件的目录,请输出目录中所有文件名完全相同的列表[重复]
MonoTouch - 如何有效地创建具有所有 CGColor 类型的列表
在列表中有效地重复data.table,从循环中的另一个data.table顺序替换具有相同名称的列