2017-03-01 70 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();