我正处于Java项目的中间,该项目将使用单词的“大字典”。 “字典”是指分配给字符串的某些数字(int)。而'big'我的意思是一个100 MB的文件。我提出的第一个解决方案可能是最简单的。在初始化时,我读入整个文件并创建一个大的HashMap,稍后将用于查看字符串。
是否有一种有效的方法可以在初始化时无需读取整个文件?也许不是,但是如果文件真的很大,那么按照可用RAM的顺序呢?所以基本上我正在寻找一种方法来在存储在内存中的大字典中有效地查找内容。
感谢到目前为止的答案,结果我意识到我可以在我的问题中更具体。正如您可能已经猜到应用程序与文本挖掘有关,特别是以稀疏向量的形式表示文本(尽管有些人有其他创造性的想法:))。因此,对于使用来说至关重要的是能够在字典中查找字符串,尽可能快地获取其密钥。只要字符串查找时间得到优化,“读取”字典文件或将其索引到数据库中的初始开销就不那么重要了。再说一次,我们假设字典大小很大,与可用RAM的大小相当。
答案 0 :(得分:3)
在非复制模式下考虑ChronicleMap
(https://github.com/OpenHFT/Chronicle-Map)。它是一个堆外Java Map
实现,或者从另一个角度来看,它是一个超轻量级NoSQL键值存储。
它对您的任务开箱即用有用:
int
或long
值只需要4(8)个字节,就像您有原始专用的地图实现一样。HashMap
和ConcurrentHashMap
答案 1 :(得分:2)
当您的数据结构与RAM的订单相差几百MB时,最好不要在运行时初始化数据结构,而是使用支持indexing
的数据库(大多数情况下都是如此)这些日子)。索引将成为您在文件变得如此之大并且正在运行JVM的 - Xmx
设置时确保最快检索文本的唯一方法之一。这是因为如果您的文件与最大尺寸设置一样大或大得多,那么您将不可避免地转到crash your JVM。
至于必须在初始化时读取整个文件。您最终必须这样做,以便您可以有效地搜索和分析代码中的文本。如果您知道您一次只会搜索文件的某个部分,则可以实施lazy loading。如果没有,你也可以咬紧牙关并将整个文件加载到数据库中。如果代码执行的其他部分不依赖于此,则可以在此过程中实现parallelism。
如果您有任何疑问,请与我们联系!
答案 2 :(得分:2)
如评论中所述,Trie
会为您节省大量内存。
您还应该考虑使用byte
而不是char
,因为这样可以为纯ASCII文本或使用国家字符集节省2倍,只要它不超过256个不同字母。
乍一看,将这种低级优化与尝试相结合毫无意义,因为节点大小由指针控制。但如果你想降低水平,就有办法。
所以对于使用来说至关重要的是能够在字典中查找字符串,尽快获得它们的密钥。
然后忘记任何数据库,因为与HashMap
相比,它们的速度很慢。
如果它不适合内存,最便宜的解决方案通常是获得更多。否则,请考虑仅加载最常用的单词并为其他单词执行较慢的操作(例如,内存映射文件)。
我被要求指出一个好的尝试实现,特别是在堆外。我不知道。
假设OP不需要可变性,特别是没有密钥的可变性,这看起来非常简单。
我想,整个字典可以很容易地打包成一个ByteBuffer
。假设主要是ASCII并且有一些黑客攻击,箭头每个箭头标签字符需要1个字节,子指针需要1-5个字节。子指针是相对的(即当前节点和子节点之间的差异),当存储在base 128 encoding中时,它们中的大多数会使它们适合单个字节。
我只能猜测总内存消耗,但我要说的是,每个字的<4字节。上述压缩会降低查找速度,但仍远不及单个磁盘访问所需的时间。
答案 3 :(得分:0)
听起来太大无法存储在内存中。要么将它存储在一个关系数据库中(简单,并且哈希上有索引,快速),或者像Solr那样存储NoSQL解决方案(小学习曲线,非常快)。
虽然NoSQL非常快,但是如果你真的想要调整性能,并且有些条目比其他条目更频繁,那么请考虑使用有限大小的缓存来保存最近使用的(例如)10000次查找。 / p>