在一个map中存储大量字符串的最节省内存的方法是什么?

6

我希望能够在一个Map<String, MagicObject>中存储大量的字符串,以便能够快速访问MagicObjects。由于这个Map中有很多条目,所以内存成为了瓶颈。假设MagicObjects无法优化,那么在这种情况下我应该使用哪种最有效的Map类型?我目前正在使用以下内容:

gnu.trove.map.hash.TCustomHashMap<byte[], MagicObject>

如果另一个地图突然使用更少的内存,我会感到惊讶,但我对优化内存使用的应用程序并不是很熟悉。 - Wesley De Keirsmaeker
2
通过切换数据结构来改变JVM内存模型是不可行的。 - duffymo
@duffymo 实际上,您可以根据使用的类型节省内存:http://java-performance.info/memory-consumption-of-java-data-types-2/(末尾的表格) - dognose
你甚至没有告诉我们你使用的是哪种Map实现。HashMap非常高效,使用String对象作为HashMap的键非常普遍,因此HashMap和String.hashCode将会被实现在一起以提供良好的性能。所以我怀疑你的Map性能不佳的说法。你可能误解了什么。 - Raedwald
使用数据库怎么样?我知道这不是你要求的东西,但这似乎像是你试图解决问题的症状而不是问题的根源。 - Ortwin Angermeier
显示剩余4条评论
3个回答

4
如果您的键足够长,并且具有许多足够长的公共前缀,则可以使用trie(前缀树)数据结构来节省内存。此问题的答案指向了几个Java trie实现。

1

为了开发思路,考虑在将字符串放入映射之前先使用Huffman编码进行压缩,只要您的字符串是固定的(字符串的数量和内容不会改变)。


-1

我来晚了,但这个问题在相关搜索中出现引起了我的兴趣。我通常不回答Java问题。

Map中有太多的条目,内存成为了瓶颈。

我对此表示怀疑。

要使内存中字符串的存储成为瓶颈,您需要有大量的唯一字符串[1]。为了让事情更清楚,我最近使用了一个包含180万个单词(180万个唯一的英文单词)的字典,在运行时它们占用了大约1.6MB的RAM。

如果您将字典中的每个单词都用作键,则仍然只使用1.6MB的RAM[2]来存储键,因此内存不能成为瓶颈。

我怀疑您正在经历字符串匹配的O(n^2)性能问题。我的意思是随着添加更多的键,性能呈指数级下降[3]。如果您使用字符串作为键,则无法避免这种情况。

如果您想加快速度,请将每个键存储到不存储重复项的哈希表中,并使用哈希键作为映射的键。

注:

[1] 我假设这些字符串都是唯一的,否则你就不会尝试将它们用作映射键了。
[2] 即使Java每个字符使用2个字节,总共也只有3.2MB的内存。
[3] 如果选择错误的数据结构来存储值,例如不平衡的二叉树,它会变得更慢。我不知道映射如何在内部存储值,但是不平衡的二叉树将具有O(2^n)的性能 - 几乎是最差的性能。

内存成为了一个瓶颈,因为应用程序的内存消耗量已经达到数百GB,其中大部分与该地图相关 - 我们确实在谈论许多许多的条目,尽管显然该地图的值也占用了相当一部分的内存,而不仅仅是字符串。关于你的建议,我确实尝试过,但后来发现使用具有自定义哈希算法的Koloboke地图实现提供了最佳的运行时性能和内存消耗混合。 - Andreas Hartmann
你的句子有些问题:“我最近使用了一个包含1.8百万个单词(1.8百万个独特的英文单词)的字典,它们在运行时占用了大约1.6MB的RAM。”要么这些单词没有同时加载到RAM中,要么它们被打包在带有某种压缩的数据结构中。无论哪种情况,唯一引用包含1.8m项的集合中的任何元素都需要至少3个字节大小的句柄,因此,如果所有这些单词都被用作映射中的键,则内存使用的绝对最小值将为5.4MB。 - Leon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接