设置大小超过500.000时,处理速度显着降低

时间:2013-12-20 20:07:55

标签: java set hashset

我不习惯使用非常大的数据集而且我有点难过。

我有以下代码:

private static Set<String> extractWords(BufferedReader br) throws IOException {
    String strLine;
    String tempWord;
    Set<String> words = new HashSet<String>();
    Utils utils = new Utils();
    int articleCounter = 0;
    while(((strLine = br.readLine()) != null)){
        if(utils.lineIsNotCommentOrLineChange(strLine)){
            articleCounter++;
            System.out.println("Working article : " + utils.getArticleName(strLine) + " *** Article #" + articleCounter + " of 3.769.926");
            strLine = utils.removeURLs(strLine);
            strLine = utils.convertUnicode(strLine);
            String[] temp = strLine.split("\\W+");
            for(int i = 0; i < temp.length; i++){
                tempWord = temp[i].trim().toLowerCase();
                if(utils.validateWord(tempWord)){
                    words.add(tempWord);
                    System.out.println("Added word " + tempWord + " to list");
                }
            }
        }
    }
    return words;
}

这基本上从BufferedReader获取一个巨大的文本文件,其中每行文本都是文章中的文本。我想在这个文本文件中列出一个独特的单词列表,但那里有3.769.926篇文章,所以字数非常大。

根据我对集合的理解,或者特别是HashSets,这应该是工作的人。一开始一切都运行得很顺利,但是在500.000篇文章之后,它开始放慢一点。当它达到700.000时,它开始变得足够缓慢,它基本上停止了两分之一,然后又重新开始。这里有一个瓶颈,我看不出它是什么......

有什么想法吗?

3 个答案:

答案 0 :(得分:5)

我相信您可能遇到的问题是哈希表(集合或映射)必须由它可以容纳的固定数量的条目支持。所以你的第一个声明可能有一个表可以容纳16个条目。抛开负载因素之类的东西,一旦你试图将17个条目放入表中,它必须增长以容纳更多条目以防止冲突,因此Java将为你扩展它。

此扩展包括创建一个包含2 * previousSize条目的新表,然后复制旧条目。因此,如果你不断扩大,你可能会最终击中一个区域,比如 524,288它将不得不增长,但它将创建一个能够处理1,048,576个条目的新表,但它必须复制整个上一个表。

如果您不介意额外的查找时间,可以考虑使用TreeSet代替HashSet。您的查找现在将是对数时间,但Tree没有预先分配的表,并且可以轻松地动态增长。要么使用它,要么声明HashSet的大小,以便它不会动态增长。

答案 1 :(得分:0)

老实说,对于那种规模,你最好转到数据库。如果您不想使用单独的Derby,可以在应用程序中嵌入Derby。

他们的索引系统针对这种规模进行了优化,而HashSet等可以应对,如果你正确按摩它们,你最好使用正确的工具。

答案 2 :(得分:0)

正如TheSageMage所指出的,随着数据的增长,HashSet实现将不断调整底层HashMap的大小。有两种方法可以解决这个问题:初始容量和负载因子。您可以使用2-arg构造函数设置它们:HashSet(int, float)。如果您知道需要的大概单词数,则可以将初始容量设置为大于该数字。这将使较小的地图工作得慢一点,但会阻止较大地图的显着减速。加载因子是在增加基础大小重新散列之前映射必须达到的完整程度。由于这对于大型地图来说是相对耗时的操作,因此您可能希望将其设置为很大一部分,比如说0.9。如果您的初始容量设置为可能超过它但不会超过该尺寸的两倍,则较大的负载系数将保证您只重复一次并尽可能晚。