2017-03-01 72 views
4

我有一个包含7.6M行的文件。每行的格式如下:A,B,C,D其中B,C,D是用于计算A的重要性级别的值,它是每行唯一的字符串标识符。我的方法是:Java HashMap vs hashset性能

private void read(String filename) throws Throwable { 
     BufferedReader br = new BufferedReader(new FileReader(filename)); 

     Map<String, Double> mmap = new HashMap<>(10000000,0.8f); 
     String line; 
     long t0 = System.currentTimeMillis(); 
     while ((line = br.readLine()) != null) { 
      split(line); 
      mmap.put(splitted[0], 0.0); 
     } 
     long t1 = System.currentTimeMillis(); 
     br.close(); 
     System.out.println("Completed in " + (t1 - t0)/1000.0 + " seconds"); 
} 

private void split(String line) { 
    int idxComma, idxToken = 0, fromIndex = 0; 
    while ((idxComma = line.indexOf(delimiter, fromIndex)) != -1) { 
     splitted[idxToken++] = line.substring(fromIndex, idxComma); 
     fromIndex = idxComma + 1; 
    } 
    splitted[idxToken] = line.substring(fromIndex); 
} 

其中虚拟值0.0被插入用于“分析”目的,并且splitted是为该类定义的简单字符串数组。我最初使用String的split()方法,但发现上述速度更快。

当我运行上面的代码时,需要12秒来解析比我认为应该花费更多的文件。如果我,例如,用一个Vector的字符串替换HashMap,并从每一行取第一个条目(即我没有放置一个相关的值,因为这应该是分期不变的),整个文件可以在小于3秒。 (我曾试图通过预先分配大小和相应地设置负载因数来最大限度地减少调整大小的次数),或者(ii)hashCode()和HashMap中的大量碰撞功能有点慢。我怀疑它(ii),因为如果我使用HashSet,可以在4秒内读取文件。

我的问题是:什么可能是HashMap执行如此缓慢的原因?对于这个尺寸的地图,hashCode()是不够的,还是有一些基本的东西我忽略了?

+1

尝试用一些静态常量最终取代你的'0.0'虚值。 '0.0'被替换为'Double.valueOf',每次创建一个新对象。而在HashSet中,只有一个预分配的虚拟对象被使用。我不确定这是什么原因,但它可以是 – esin88

+0

'splitted []'的最后一个元素将始终保存整行。这不是你想要的。 – EJP

+0

'HashSet'由内部的'HashMap'支持,所以唯一的区别就是你的虚拟'0.0'的自动装箱。 – bashnesnos

回答

2

HashMap vs Vector:在HashMap中插入比在Vector中插入更昂贵。尽管两者都是分期付款的恒定时间操作,但HashMap在内部执行许多其他操作(例如生成hashCode,检查collisions,解决collisions等),而Vector仅在最后插入元素(增加结构的大小,如果需要)。

HashMap vs HashSet: HashSet内部使用HashMap。因此,如果您将它们用于相同目的,则不应有任何性能差异。理想情况下,这两者都有不同的目的,所以关于哪个更好的讨论是无用的。因为你需要B,C,D作为A的值,所以你应该坚持HashMap。如果你真的只想比较性能,把所有键的值设置为“null”而不是0.0(因为这是HashSet在将键放入其支持的HashMap中时使用的值)。

更新:HashSet使用一个虚拟常量值(static final)插入到HashMap中,而不是null。对于那个很抱歉。你可以用任何常量代替你的0.0,性能应该和HashSet类似。

0

是的,检查你的例子0.0作为虚拟值VS静态最终常数作为虚拟值VS HashSet。这是粗略的比较,为了更好的精度,我建议使用JHM工具,但是我的HashSet性能与虚拟性能的静态常数几乎相同。

所以,最有可能,即低性能被包裹你的0.0虚值的每一行(它是由Double.valueOf()汇编,其中明确创建一个新的Double对象每次更换期间)引起的。

这将解释低性能,因为HashSet有预定义的静态最终虚拟对象(它不是null,btw)。

2

您可以使用更高效的存储库集合库。

我建议Eclipse Collections(https://www.eclipse.org/collections/),它有一个ObjectDoubleMap(https://www.eclipse.org/collections/javadoc/8.0.0/org/eclipse/collections/api/map/primitive/ObjectDoubleMap.html),它是一个double(yes,primitive double)作为关联值的对象(在你的情况下为String)的映射。处理内存和性能要好得多。

您可以通过执行获得的这一个空的实例:

ObjectDoubleMaps.mutable.empty();