2013-07-10 59 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