Java:优化哈希集以进行大规模重复检测

世界无尽:

我正在做一个处理大量推文的项目;目标是在我处理重复项时将其删除。我有tweet ID,这些ID以格式的字符串形式出现"166471306949304320"

我一直在使用HashSet<String>它,它工作了一段时间。但是,当我得到大约一千万个项目时,我陷入了极大的泥潭,最终出现了GC错误,大概是由于重新哈希处理造成的。我尝试定义一个更好的尺寸/负载

tweetids = new HashSet<String>(220000,0.80F);

这样就可以使它走得更远,但是仍然非常慢(大约1000万,处理时间是原来的3倍)。我该如何优化呢?假设我大概知道到最后应该在集合中包含多少个项目(在这种情况下,大约为20-22百万),那么我应该创建一个仅刷新2到3次的HashSet,或者这样的开销设置会招致太多的时间处罚吗?如果我不使用String,或者定义了另一个HashCode函数(在特定的String实例中,我不确定该怎么做),情况会更好吗?实施代码的这一部分如下。

tweetids = new HashSet<String>(220000,0.80F); // in constructor
duplicates = 0;
...
// In loop: For(each tweet)
String twid = (String) tweet_twitter_data.get("id");
// Check that we have not processed this tweet already
if (!(tweetids.add(twid))){
    duplicates++;
    continue; 
}

感谢您的建议,我解决了。问题在于散列表示形式所需的内存量;首先,它HashSet<String>是巨大的并且是不需要的,因为在String.hashCode()这种规模下,它是过高的。接下来,我尝试了一个Trie,但失败了,刚超过100万个条目。重新分配数组是有问题的。我使用了HashSet<Long>更好的效果并几乎达到了效果,但是速度下降了,最终在处理的最后一站(大约1900万)崩溃了。解决方案是从标准库中删除并使用Trove与根本不检查重复项相比,几分钟就可以完成2200万条记录。最终实现很简单,看起来像这样:

import gnu.trove.set.hash.TLongHashSet;
...
    TLongHashSet tweetids; // class variable
... 
    tweetids = new TLongHashSet(23000000,0.80F); // in constructor
...
    // inside for(each record)
    String twid = (String) tweet_twitter_data.get("id");
    if (!(tweetids.add(Long.parseLong(twid)))) {
        duplicates++;
        continue; 
    }
吉尔斯·范古普(Jilles van Gurp):

您可能想要超越Java集合框架。我已经完成了一些占用大量内存的处理,您将遇到几个问题

  1. 大型哈希图和哈希集的存储桶数量将导致大量开销(内存)。您可以通过使用某种自定义哈希函数和例如50000的模来影响此
  2. 字符串在Java中使用16位字符表示。您可以通过对大多数脚本使用utf-8编码的字节数组将其减半。
  3. HashMap通常是非常浪费的数据结构,而HashSet基本上只是围绕它们的薄包装。

鉴于此,看看替代的番石榴或番石榴。另外,您的ID看起来像多头。它们是64位,比字符串表示形式小很多。

您可能要考虑的替代方法是使用布隆过滤器(番石榴具有不错的实现)。布隆过滤器会告诉您是否肯定不包含某物,并且如果包含某物,则具有合理的确定性(小于100%)。结合一些基于磁盘的解决方案(例如数据库,mapdb,mecached等)应该可以很好地工作。您可以缓冲传入的新ID,将其批量写入,然后使用Bloom筛选器检查是否需要查看数据库,从而在大多数情况下避免进行昂贵的查找。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章