对可变长度数据要考虑的散列算法

时间:2012-12-17 13:20:28

标签: java algorithm hash text-files

为了避免任何混淆,我根据我对散列算法的研究重新构建了我的问题

问题陈述 我有多个包含可变长度数据记录的文本文件。我需要查找输入中是否有重复记录。每个文本文件都可以有数百万的数据记录。

由于我无法一次将所有数据加载到内存中,因此我计划在处理时创建记录中关键字段的哈希值。处理记录意味着验证,过滤和转换它。处理完所有文本文件中的所有记录后,它们将合并以创建整个输入的一个视图(文本文件或数据库表)。

如果为所有记录生成哈希值,则查找重复项会快得多。如果存在哈希值冲突,则只能检查那些记录的相等性(假设哈希算法是确定性的)

问题 - 对于这样的输入,我应考虑哪些哈希算法,即可变长度数据?

2 个答案:

答案 0 :(得分:4)

简答

不要这样做。使用Java地图。你可以在这里找到细节: http://docs.oracle.com/javase/6/docs/api/java/util/Map.html

长答案

您可以通过将字符串视为base-N中的数字来创建完美的散列函数,其中N是任何字符可以采用的所有可能值。这里的问题是记忆。散列函数意味着与数组一起使用,这意味着你需要一个足够大的数组来处理散列的结果,这是不切实际的。

例如,采用10个字符键的适度示例。让我们更谦虚,并假设它们只保证由小写字母组成。这为每个角色提供了26种可能性,以及10个字符。这意味着可能的组合是:

26 ^ 10 = 141,167,095,653,376

如果你查找散列算法,它们首先要包括的是碰撞检测,因为它们承认碰撞是生活中的事实。

现在你说你没有在内存中加载密钥,但为什么你使用哈希呢?散列的要点是为您提供映射到数组索引。也许你最好使用另一种机制。

可能的解决方案

如果您担心内存,请获取有关文件中重复项的统计信息。如果您只存储一个标志来指示哈希中特定键的出现,并且您有许多重复项,那么您可以只使用Java的映射。 Java的映射处理冲突,因此不会阻止您检测唯一键。您可以放心,如果找到A [x],则表示x在A中,即使x的哈希与先前的哈希冲突也是如此。

接下来,您可以尝试使用一些实用程序来提取重复项。由于它们是专门为此目的而编写的,因此它们应该能够处理大量数据。

最后,您可以尝试将您的条目放入数据库并使用它来处理重复项。这似乎有点矫枉过正,但数据库已经过优化,可以处理大量的记录。

答案 1 :(得分:1)

这是Map理念的扩展。在诉诸于此之前,我会通过简单地构建一个表示所有字符串的HashSet来检查它是不是可以完成的。请记住,您可以使用64位JVM并设置较大的堆大小。

定义一个StringLocation类,其中包含对磁盘上的字符串进行随机访问所需的数据 - 例如,对RandomAcessFile的引用和文件中的偏移量。如果您无法一次打开所有文件,请根据需要打开和关闭,为最常用的文件缓存RandomAcessFile。

创建HashMap<Integer,List<StringLocation>>

开始阅读字符串。对于每个字符串,转换为小写并以整数形式获取其hashCode(),hash。如果Map中有一个带有hash作为键的条目,则将新字符串与现有值中表示的每个字符串进行比较,对随机文件进行访问以获取已处理的字符串。使用String equalsIgnoreCase。如果匹配,则表示您有重复。如果没有匹配项,请将表示当前字符串的新StringLocation附加到List。

这一次最多需要两个字符串在内存中,你当前正在处理的字符串和一个先前处理过的字符串与你要比较的hashCode()结果相同。

通过使用MessageDigest为小写字符串生成具有较低冲突风险的宽校验和,并将其保存在StringLocation中,可以进一步减少重新读取字符串以进行等于检查的次数宾语。在比较期间,如果校验和不匹配则返回false,而不重新读取字符串。