在Java中创建一个非常非常大的地图

5
使用Java,我希望创建一个可以无限增长并且可能比可用内存大得多的Map。显然,使用标准的POJO HashMap会耗尽内存,导致JVM崩溃。所以我的想法是创建一种Map,如果它注意到内存不足,它就可以将当前内容写入磁盘。
有没有人实现过类似这样的东西或知道任何现有的解决方案?
我的目标是读取一个非常大的ASCII文件(例如50GB),每次读取一行。每行包含一个键和一个值。文件中的键可以重复。然后我会将每行存储在一个Map中,它是Key到Value列表的映射。这个Map对象将会持续增长。
非常感谢任何建议。
Phil
更新:
感谢大家所有的评论和建议。对于我描述的问题,数据库是正确的、可扩展的解决方案。我应该说明这是一个需要创建和使用一段时间来帮助解析文件的暂时性Map。在这种情况下,Michael建议“仅存储行号而不是实际值”是最合适的。将Michael的答案标记为推荐解决方案。

4
使用内存数据库,比如HSQL,会不会更简单一些? - mcfinnigan
我不太喜欢数据库的方法。它太过笨重了。 - Phil
示例键:'A:B:C:D:E',示例值:'Adam;Bob;Charles; "Dog Dog Dog Dog; Dog"; Elephant - 从中可以看出,我还在处理CSV文件。 - Phil
解析文件后,您想对地图进行什么操作?将其保留在内存中以供读取访问吗? - home
一旦原文件被解析,我想将其转储到另一个文件中,其中相同的键被分组在一起。还有每个原始文件中的“值”的格式化(但那真的不相关)。 - Phil
显示剩余4条评论
8个回答

14

我认为您正在寻找一种数据库。


1
嗯,我确实考虑过使用数据库,但我只想要一个非常简单的东西(像Map一样),可以根据需要溢出到磁盘上。问题在于我们如何知道键是否存在,因此可能只需要将Map的“值”部分溢出即可。 - Phil
可能的解决方案:使用一个映射(map),但只将实际值的行号作为值存储。然后,你可以使用行号从文件中检索实际值。 - michael667
Michael - 我确实考虑过使用RandomAccessFile读取器来读取文件。但问题在于BufferedReader无法提供文件的当前读取位置。放弃BufferedReader意味着失去了readLine的能力,这也会影响到我的CSV解析(由于这不是原始问题的关键点,所以我没有提到)。 - Phil

3

一个NoSQL数据库很可能很容易设置,并且更像一个映射。 检查Oracle的BerkeleyDB Java版本。 它具有类似地图的接口,可嵌入,因此不需要复杂的设置。


2
听起来像是将大文件倒入数据库。
嗯,我曾经遇到过类似的情况。但是,在我的情况下,所有内容都是以TXT文件格式存在的,并且整个文件都有相同的格式化行。因此,我只是将文件分成了几个部分(可能是我的JVM可以处理的最大大小),然后逐个调用文件进行处理。
另一种方法是直接将数据加载到数据库中。

2
严肃点说,按建议选择一个简单的数据库。这不会增加负担——你不必使用JPA或其他东西,只需使用本地SQL的JDBC。例如,Derby或HSQL可以在嵌入式模式下运行,无需定义用户、访问权限或单独启动服务器。
当你深入到哈希映射解决方案时,“负担”会在你背后捅你一刀,因为你需要另一个优化来避免OutOfMemoryException,或者文件不是50GB,而是75GB……真的,不要走那条路。

0

我使用BerkleyDB来完成这个功能,尽管它比Map更复杂(尽管他们有一个Map包装器,但我不建议除了简单的应用以外的其他用途)

http://www.oracle.com/technetwork/database/berkeleydb/overview/index.html

它也可以在Maven中获取http://www.oracle.com/technetwork/database/berkeleydb/downloads/maven-087630.html

  <dependencies>
    <dependency>
      <groupId>com.sleepycat</groupId>
      <artifactId>je</artifactId>
      <version>3.3.75</version>
    </dependency>
  </dependencies>

  <repositories>
    <repository>
      <id>oracleReleases</id>
      <name>Oracle Released Java Packages</name>
      <url>http://download.oracle.com/maven</url>
      <layout>default</layout>
    </repository>
  </repositories>

它还有另一个缺点,即供应商锁定(即您被迫使用此工具,尽管可能存在其他映射包装器可用于其他数据库)

因此,根据您的需求进行选择。


0

大多数缓存API都像映射一样工作,并支持溢出到磁盘。例如,Ehcache就支持这一点。或者可以参考这个Guava教程


0

如果你只是想为数据处理构建地图(而不是响应请求的随机访问),那么MapReduce可能是你想要的,而无需使用数据库。

编辑:请注意,尽管许多MapReduce介绍都侧重于运行许多节点的能力,但你仍然可以从避免在一台机器上将所有数据保存在内存中的要求中获益。


0

你有多少内存?除非你有足够的内存来保持大部分数据在内存中,否则它会变得非常慢,甚至可能失败。一个频繁进行页面交换的程序可能会慢1000倍或更多。一些电脑有16-24 GB的内存,你可能需要考虑增加内存。

假设有足够的重复数据,你可以将大部分数据保存在内存中。我建议你使用自己制作的基于字节的字符串类,因为你有ASCII数据,并且将你的值存储为另一种这样的“String”类型(带有分隔符)。你可能会发现你可以将工作数据集保存在内存中。


如果你想选择更轻量级的字符串路线,我建议使用 MutableString 类 - 它部分地是为此目的设计的。 - Timothy Jones
MutableString使用char[],即使String也可以将ascii字符串转换为byte[],在较新的JVM上默认使用-XX:+UseCompressedStrings。但是它并不像你自己做得那么高效。 - Peter Lawrey

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