我正在处理一个处理大量推文的项目;目标是在我处理重复项时删除它们。我有推文 ID,它以格式字符串的形式出现"166471306949304320"
我一直在使用HashSet<String>
这个,它工作了一段时间。但是当我达到大约 1000 万个项目时,我彻底陷入困境并最终得到一个 GC 错误,大概来自重新散列。我尝试定义更好的尺寸/负载
tweetids = new HashSet<String>(220000,0.80F);
这让它走得更远一点,但仍然非常缓慢(大约 1000 万,它需要 3 倍的时间来处理)。我该如何优化呢?鉴于我大概知道最后应该有多少项目(在这种情况下,大约 20-22 百万)我应该创建一个只重新散列两次或三次的 HashSet,或者这样的开销设置招致太多的时间惩罚?如果我不使用字符串,或者如果我定义不同的 HashCode 函数(在这种情况下是字符串的特定实例,我不知道该怎么做),事情会更好吗?这部分实现代码如下。
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;
}