Java 当集合大小超过500.000时,处理速度明显减慢

Java 当集合大小超过500.000时,处理速度明显减慢,java,set,hashset,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();

我不习惯于处理非常大的数据集,我在这里有点不知所措

我有以下代码:

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 br)引发IOException{
弦斯特林;
字符串临时字;
Set words=新HashSet();
Utils Utils=新Utils();
int articleccounter=0;
而(((strLine=br.readLine())!=null)){
if(utils.lineIsNotCommentOrLineChange(strLine)){
articleCounter++;
System.out.println(“工作文章:+utils.getArticleName(strLine)+”***文章#“+articleCounter+”of 3.769.926”);
strLine=utils.removeURL(strLine);
strLine=utils.convertUnicode(strLine);
字符串[]temp=strLine.split(\\W+);
对于(int i=0;i
这基本上是从BufferedReader获取一个巨大的文本文件,其中每行文本都是一篇文章中的文本。我想在这个文本文件中列出一些独特的单词,但是其中有3.769.926篇文章,因此单词数量非常庞大

从我对集合的理解来看,或者具体地说,hashset,可以说,这应该是适合这项工作的人。一开始一切都很顺利,但在写了50万篇文章之后,速度开始放慢。当它达到700000时,它开始变得足够慢,以至于它基本上停止了两秒钟,然后再继续。这里的某个地方有个瓶颈,我看不出它是什么


有什么想法吗?

我认为您可能面临的问题是,哈希表(集合或映射)必须由它所能容纳的固定数量的条目来支持。因此,您的第一个声明可能有一个能够容纳16个条目的表。抛开负载因素等因素不谈,一旦您尝试将17个条目放入表中,它就必须增长以容纳更多条目以防止冲突,因此Java将为您扩展它

此扩展包括使用
2*previousSize
条目创建一个新表,然后复制旧条目。所以,如果你不断扩张,你可能最终会碰到一个区域,比如 524288,但它将创建一个能够处理1048576个条目的新表,但它必须复制整个上一个表


如果您不介意额外的查找时间,您可以考虑使用
TreeSet
而不是
HashSet
。您现在可以使用对数时间进行查找,但是
树没有预先分配的表,可以轻松地动态增长。使用此选项,或者声明
哈希集的大小,使其不会动态增长

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


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

正如管理层所指出的,HashSet实现将随着数据的增长不断调整基础HashMap的大小。有两种方法可以解决这个问题:初始容量和负载系数。您可以使用2-arg构造函数设置这两个参数:。如果您知道需要的字数,可以将初始容量设置为大于该数字。这将使较小的地图工作稍微慢一点,但会防止较大地图的急剧减速。负载因子是在增加基础大小重新灰化之前贴图必须达到的满度。由于对于大型贴图来说,这是一个相对耗时的操作,因此可能需要将其设置为较大的分数,例如0.9。如果您的初始容量设置为可以超过它,但永远不会超过该容量的两倍,则较大的负载系数将保证您只重新刷新一次,并且尽可能晚。

哈希集由哈希映射支持,一旦你增长到一个大的值,它就必须开始对它的数据进行深度复制,以确保碰撞不会变得荒谬可笑。高冲突计数最终将使您的固定时间执行收集转变为线性执行。如果正确调整表的大小,它将在内存和性能之间进行适当的权衡,从而更有效地运行smoothness@GregGiacovelli只是为了确保我理解你的建议;他应该使用HashSet(int initialCapacity)构造函数,其中initialCapacity相当高?甚至可能使用Integer.MAX_值?您必须根据自己的需要找出最适合自己的方法。不确定这些对象有多大,但也可能值得将loadfactor更改为不像其他人所说的那样具有攻击性,您可能不希望所有这些都存储在内存中;)下面是一些有关集合实现的有用信息:。。。把每个字都写进数据库来降低速度@AmitSharma您可以将DB写入缓存到一个批处理中,这实际上会非常快。它还允许您在重新填充下一个缓冲区时在单独的线程中进行写入。如果您正在写入Derby数据库,那么它也会在同一个Java进程中进行,速度非常快。此外,gc可能会频繁发生,并导致速度缓慢。将堆大小增加到几GB,并确保运行64位版本的Java.Correct。我是从纯编程的角度考虑的,但是也可以通过JVM优化来加快速度。