什么是最快的散列algorithm来检查两个文件是否相等?
什么是创build散列函数的最快方法,用来检查两个文件是否相等?
安全不是很重要。
编辑:我通过networking连接发送文件,并确保两边的文件是平等的
一种方法可能是使用一个简单的CRC-32algorithm,并且只有当CRC值比较相等时,用SHA1或更强健的东西重新运行散列。 一个快速的CRC-32将在任何一天超过密码安全散列。
除非您使用的是非常复杂和/或较慢的哈希,否则从磁盘加载数据比计算哈希要花费更多的时间(除非使用RAM磁盘或顶级SSD)。
所以要比较两个文件,使用这个algorithm:
- 比较大小
- 比较date(在这里要小心:这可以给你一个错误的答案;你必须testing这是否是你的情况)
- 比较哈希
这允许快速失败(如果大小不同,您知道文件是不同的)。
为了使事情更快,您可以计算散列一次并将其与文件一起保存。 还要将文件date和大小保存到这个额外的文件中,以便在主文件更改时必须快速重新计算散列或删除散列文件。
xxhash声称自己是相当快速和强大的,碰撞明智的:
http://cyan4973.github.io/xxHash/
在64位处理器上运行的64位variables比32位运行速度更快。
http://code.google.com/p/crcutil也被认为是相当快的(并且利用硬件CRC指令,如果存在的话,这些指令可能非常快,但是如果你没有支持它们的硬件,一样快)。; 不知道CRC32c是否像xxHash一样是散列(就冲突而言)…
https://code.google.com/p/cityhash/似乎与crcutil相似,因为它可以编译为使用硬件CRC32c指令(如果指导的话)。;
如果你“只想要最快的原始速度”,并不关心散列输出随机分布的质量(例如,小集合,或速度至关重要),有一些快速algorithm在这里提到: http ://www.sanmayce.com/Fastest_Hash/ (这些“不是很随机的”分布typesalgorithm在某些情况下“足够好”,而且速度很快)。 显然FNV1A_Jesteress
是“长”string中最快的,其他string可能是小string。 http://locklessinc.com/articles/fast_hash/也似乎相关。; 我没有研究,看看这些是什么碰撞属性。
你为什么要散列呢?
如果你想确保两个文件是相等的,那么根据定义,你将不得不读取整个文件(除非它们实际上是相同的文件,在这种情况下,你可以通过查看文件系统上的元数据来判断)。 无论如何,没有理由哈希,只是读过他们,看看他们是否是相同的。 散列会使效率降低。 即使哈希匹配,你仍然不确定文件是否相等。
编辑:这个答案已经发布之前,问题指定了任何有关networking。 它只是询问比较两个文件。 现在我知道这些文件之间有一个networking跳转,我只能说使用MD5哈希值并完成它。
如果只有一个,那么你必须读取这两个文件来产生这两个文件的散列,为什么不一次只读一小段文件,然后比较呢?
如果不是CRC是一个非常简单的algorithm。
你可以尝试MurmurHash ,这是专门devise的快速,而且非常简单的代码。 如果MurmurHash返回一个匹配,你可能想和第二个更安全的散列,只是为了确保。
对于这种types的应用程序, Adler32可能是最快的algorithm,具有合理的安全级别。 对于较大的文件,您可以计算多个散列值,例如每个文件5 MB的块,从而减less错误的可能性(即散列相同但文件内容不同的情况)。 而且,这个多哈希值设置可以允许以multithreading方式实现哈希的计算。
编辑 :(斯蒂文·苏提特的话)
谨慎的话,如果文件很小!
Adler32的“密码”属性,或者说它的弱点对于短消息而言是众所周知的。 出于这个原因,对于小于几千字节的文件,应该避免所提出的解决scheme。
在这个问题上,OP总是明确地寻求一种快速algorithm,并放弃对安全性的担忧 。 此外,对速度的追求可能振振有词地暗示着一个人正在处理“大”文件而不是小文件 。 在这种情况下,可能并行应用5Mb文件块的Adler32仍然是非常有效的答案。 Alder32以其简单和速度而闻名。 另外,它的可靠性虽然低于相同长度的CRC,但是对于超过4000字节的消息是完全可以接受的。
我们在这里优化的是花在一项任务上的时间。 不幸的是,我们对手头的任务知之甚less,不知道最佳解决scheme应该是什么。
是一次比较两个任意文件吗? 然后比较大小,然后简单地比较文件,如果这对你的IO更好,则逐字节地(或者MB地)进行比较。
如果是2套大文件,或者多套文件,那不是一次性的练习。 但是会经常发生的事情,那么应该为每个文件存储哈希值。 一个散列从来不是唯一的,但是一个有9位数字(32位)的散列对于大约40亿个组合是有好处的,而一个64位数字将足够区分一些16 * 10 ^ 18个不同文件。
一个体面的妥协将是为每个文件生成2个32位哈希值,一个为8k,另一个为1MB + 8k,将它们作为一个64位数字打包。 将所有现有的文件编目到数据库中应该相当快,而且在这个数据库中查找候选文件也应该很快。 一旦匹配,确定它们是否相同的唯一方法是比较整个文件。
我相信给予人们他们需要的东西,这并不总是他们认为他们需要的东西,或者他们想要的东西。
在任何情况下,你应该完全读取每个文件(大小不匹配的情况除外),所以只需读取文件和比较块到块。
使用散列只是获得CPU使用率,没有更多。 因为你什么都不写,操作系统的caching会有效的删除你读的数据,所以在Linux下只需使用cmp工具即可
以下是从我的个人项目中查找重复文件的代码,以排除也重复删除重复的图片。 根据我的经验,首先使用像CRC32这样的快速哈希algorithm,然后使用MD5或SHA1的速度更慢,而且没有任何改进,因为大部分相同大小的文件确实是重复的,所以从CPU时间angular度看, ,这种方法可能对所有types的项目都是不正确的,但是对于图像文件来说是绝对正确的。 在这里,我只对相同大小的文件进行MD5或SHA1哈希处理。
PS:它依靠Apache commons编解码器来有效地生成哈希。
示例用法: new DuplicateFileFinder(“MD5”)。findDuplicateFilesList(filesList);
import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.codec.digest.DigestUtils; /** * Finds the duplicate files using md5/sha1 hashing, which is used only for the sizes which are of same size. * * @author HemantSingh * */ public class DuplicateFileFinder { private HashProvider hashProvider; // Used only for logging purpose. private String hashingAlgo; public DuplicateFileFinder(String hashingAlgo) { this.hashingAlgo = hashingAlgo; if ("SHA1".equalsIgnoreCase(hashingAlgo)) { hashProvider = new Sha1HashProvider(); } else if ("MD5".equalsIgnoreCase(hashingAlgo)) { hashProvider = new Md5HashProvider(); } else { throw new RuntimeException("Unsupported hashing algorithm:" + hashingAlgo + " Please use either SHA1 or MD5."); } } /** * This API returns the list of duplicate files reference. * * @param files * - List of all the files which we need to check for duplicates. * @return It returns the list which contains list of duplicate files for * eg if a file a.JPG have 3 copies then first element in the list * will be list with three references of File reference. */ public List<List<File>> findDuplicateFilesList(List<File> files) { // First create the map for the file size and file reference in the array list. Map<Long, List<File>> fileSizeMap = new HashMap<Long, List<File>>(); List<Long> potDuplicateFilesSize = new ArrayList<Long>(); for (Iterator<File> iterator = files.iterator(); iterator.hasNext();) { File file = (File) iterator.next(); Long fileLength = new Long(file.length()); List<File> filesOfSameLength = fileSizeMap.get(fileLength); if (filesOfSameLength == null) { filesOfSameLength = new ArrayList<File>(); fileSizeMap.put(fileLength, filesOfSameLength); } else { potDuplicateFilesSize.add(fileLength); } filesOfSameLength.add(file); } // If we don't have any potential duplicates then skip further processing. if (potDuplicateFilesSize.size() == 0) { return null; } System.out.println(potDuplicateFilesSize.size() + " files will go thru " + hashingAlgo + " hash check to verify if they are duplicate."); // Now we will scan the potential duplicate files, and eliminate false positives using md5 hash check. List<List<File>> finalListOfDuplicates = new ArrayList<List<File>>(); for (Iterator<Long> potDuplicatesFileSizeIterator = potDuplicateFilesSize .iterator(); potDuplicatesFileSizeIterator.hasNext();) { Long fileSize = (Long) potDuplicatesFileSizeIterator.next(); List<File> potDupFiles = fileSizeMap.get(fileSize); Map<String, List<File>> trueDuplicateFiles = new HashMap<String, List<File>>(); for (Iterator<File> potDuplicateFilesIterator = potDupFiles.iterator(); potDuplicateFilesIterator .hasNext();) { File file = (File) potDuplicateFilesIterator.next(); try { String md5Hex = hashProvider.getHashHex(file); List<File> listOfDuplicatesOfAFile = trueDuplicateFiles.get(md5Hex); if (listOfDuplicatesOfAFile == null) { listOfDuplicatesOfAFile = new ArrayList<File>(); trueDuplicateFiles.put(md5Hex, listOfDuplicatesOfAFile); } listOfDuplicatesOfAFile.add(file); } catch (IOException e) { e.printStackTrace(); } } Collection<List<File>> dupsOfSameSizeList = trueDuplicateFiles.values(); for (Iterator<List<File>> dupsOfSameSizeListIterator = dupsOfSameSizeList.iterator(); dupsOfSameSizeListIterator .hasNext();) { List<File> list = (List<File>) dupsOfSameSizeListIterator.next(); // It will be duplicate only if we have more then one copy of it. if (list.size() > 1) { finalListOfDuplicates.add(list); System.out.println("Duplicate sets found: " + finalListOfDuplicates.size()); } } } return finalListOfDuplicates; } abstract class HashProvider { abstract String getHashHex(File file) throws IOException ; } class Md5HashProvider extends HashProvider { String getHashHex(File file) throws IOException { return DigestUtils.md5Hex(new FileInputStream(file)); } } class Sha1HashProvider extends HashProvider { String getHashHex(File file) throws IOException { return DigestUtils.sha1Hex(new FileInputStream(file)); } } }
你可以看看samba / rsync开发者使用的algorithm。 我没有深入地看过,但是我一直看到它。 显然是相当不错的。