2013-07-10 60 views
1

我有一個字典作爲文本文件從2M字映射到50k字。我通過逐行讀取文件,在分隔符上分割並調用myMap.put(line[0], line[1]),將此文件加載到內存中作爲HashMap<String, String>。文本文件的大小爲45MB,而HashMap使用堆的350MB。我的目標是減少內存使用,而不會影響查找速度。 myMap.values().size()返回2M而不是50k,表明這些值存儲爲重複值。有沒有辦法讓相同的值指向同一個String對象?存儲在HashMap中的重複值

Map<String, String> dict = new HashMap<>(); 
try (FileReader fr = new FileReader(FILE); 
     BufferedReader br = new BufferedReader(fr)) { 
    String line; 
    while ((line = br.readLine()) != null) { 
     String key_value[] = line.split(":"); 
     dict.put(key_value[0], key_value[1].intern()); 
    } 
} catch (Exception e) { 
    e.printStackTrace(); 
} 
+5

如果你有2M獨特的單詞映射到50k(非唯一)的話,那麼你hashmap的大小將是2M。 – assylias

+1

hashmaps大小是基於條目,因此鍵的數量。關於重複值:JVM使用字符串值進行一些優化。由於字符串是不可變的,它通常對同等的字符串使用相同的對象。你不能依賴那個,但可能你的字符串已經不重複了。 –

+0

@assylias我知道。我的問題是如何避免存儲重複值。這是允許多個鍵指向映射到相同的對象值。 – mossaab

回答

2

您可以在值使用String.intern(),使它們都指向同一個實例。但是這有其他的問題,比如使用PermGenSpace,它不是Java之前的垃圾收集器。 你會這樣稱呼它:myMap.put(line[0], line[1].intern())

也許一張基於trie的地圖更高效,但我還沒有使用過。還取決於你的字符串的性質。密鑰越相似,特洛伊可以節省的空間就越多。

http://code.google.com/p/trie-map/

另請參閱有關keys().size()values().size()Dukeling's answer和使用另一個地圖,以避免重複的值。

+0

我在Java 1.7上,剛剛嘗試過'行[ 1] .intern()'。 'myMap.values()。size()'仍然返回'2M',並且內存使用保持不變。如果沒有提供規範的解決方案,我會嘗試'trie'。 – mossaab

+2

+1另一種方法是有一個'Map ',其中的鍵和值是相同的。您可以查看該值以查看它之前是否已被使用並重用相同的String對象。當你完成時,這個「interner」地圖可以被丟棄。 –

+1

@mossaab'myMap.values()。size()'將永遠*如果有2M個鍵,則返回2M。 – assylias

5

無論是否重複指向相同的對象,仍然需要引用這些對象,因此size仍應返回包含重複項的大小。

A simple example showing this

如果您希望重複指向相同的對象,則必須在HashMap之外執行此操作,或者希望優化器處理它。

替代String.intern()joe776 suggested有可能與延伸的自我書面收集一些Set(因爲Set沒有Object get(Object)法)或其他HashMap(有對象指向自己),它允許你去的一個參考共同的目標。

+0

我投這個答案。不過,我首先回答了joe776。 – mossaab