在开始解释我的问题之前,我应该提一下,我不是在寻找增加Java堆内存的方法。我应该严格存放这些物品。
我正在努力将大量(5-10 GB)的DNA序列及其计数(整数)存储在哈希表中。 DNA序列(长度为32或更短)由'A','C','G','T'和'N'(未定义)字符组成。众所周知,当在内存中存储大量对象时,与C和C ++等低级语言相比,Java的空间效率较差。因此,如果我将此序列存储为字符串(对于长度为30的序列,它保存大约100 MB内存),我会看到错误。
我试图将核酸表示为'A'= 00,'C'= 01,'G'= 10,'T'= 11并忽略'N'(因为它将char烧成2位变换为第5个酸)。然后,将这些2位酸连接成字节数组。它带来了一些改进但不幸的是我再次看到错误几个小时后。我需要一个方便的解决方案或至少一种解决方法来处理此错误。提前谢谢。
答案 0 :(得分:2)
我建议您查看Trove4j Collections API;它提供了包含原语的集合,这些原语将比它们的盒装包装类使用更少的内存。
具体来说,您应该查看他们的TObjectIntHashMap
。
此外,我建议不要将String
或char
存储为JDK 9,因为char
的后备String
数组是UTF-16编码,每byte
使用两个char
s。 JDK 9默认为UTF-8,其中只使用一个byte
。
答案 1 :(得分:2)
相当复杂,这可能是一个奇怪的想法,需要做很多工作,但这就是我想要的:
您已经指出了整个任务的两个单独的子问题:
HashMap
实施可能不是最理想的我建议为Map<String, Long>
接口编写一个高度定制的哈希映射实现。在内部,您不必存储字符串。不幸的是5 ^ 32&gt; 2 ^ 64,所以没有办法将整个字符串打包成一个长的,好吧,让我们坚持两个长的钥匙。在为地图实现提供字符串键时,可以相当有效地进行字符串/返回长[2]转换(使用位移等)。
关于打包值,以下是一些注意事项:
对于键值对,标准hashmap需要有一个N long数组用于存储桶,其中N是当前容量,当从散列键中找到存储桶时,它需要具有密钥链表-value对用于解析生成相同哈希码的密钥。对于您的具体情况,您可以尝试以下列方式对其进行优化:
3N
的long [],其中N
是以连续数组存储键和值的容量3 * (hashcode % N)
和3 * (hashcode % N) + 1
存储密钥的长[2]表示,匹配此存储桶或唯一存储的第一个密钥(在插入时,否则为零),在位置3 * (hashcode % N) + 2
存储相应的计数HashMap<Long2KeyWrapper, Long>
中。我们的想法是保持上面提到的数组的容量(并相应地调整大小)足够大到目前为止该连续数组中的大部分数据而不是后备散列映射。这将大大减少hashmap的存储开销鉴于不平等5^32 > 2^64
你的想法使用位来编码5个字母似乎是我现在能想到的最好的。使用3位并相应地长[2]。
答案 2 :(得分:0)
如果您正在使用~10gb数据的顺序,或者至少使用内存表示大小为~10gb的数据,那么您可能需要考虑如何编写您不需要的数据。目前需要磁盘并将数据集的各个部分加载到内存中以对其进行处理。
几年前,当我使用蒙特卡罗模拟进行研究时,我遇到了这个问题,所以我写了Java data structure来解决它。您可以在此处克隆/分叉源代码:github.com/tylerparsons/surfdep
该库支持MySQL和SQLite作为底层数据库。如果您没有,我建议使用SQLite,因为它设置起来要快得多。
完全免责声明:这不是最有效的实现,但如果让它运行几个小时,它将处理非常大的数据集。我在Windows笔记本电脑上使用多达10亿个元素的矩阵成功测试了它。