我的程序需要向数据容器插入数百万条记录。我尝试了哈希图和树状图。两者都会给我堆空间例外,尽管我允许 JVM 使用 2gb 内存。
我的程序经常从容器中获取特定数据,我认为如果需要 O(logn) 时间对我来说是可以接受的。那么我应该使用什么容器呢?或者我需要实现一个?如何?
更多细节:键是字符串,就像一个全局 ID,例如"00011123459"像这样。然后键将映射到列表列表,即List<List<String>>
。我的程序从文件中读取Line,然后将行更改为列表,然后从列表中获取全局id,然后将列表放入相应的列表列表中。该文件有超过数百万行,这就是为什么我认为主要原因是我创建了太多列表。但是,我无法向计算机添加更多内存。
HashMap
占用的内存比TreeMap
少,并且是O(1)。
如果您的密钥是数字,您可以使用 Trove4j 中的TLongObjectHashMap
节省内存。
另一种选择是使用 MapDB 将数据临时保存在磁盘上。
您还可以在 Guava 中使用 CacheBuilder
应用缓存:当 Java 中的集合超出容量时会发生什么?
假设绝大多数内存使用是由于记录数据本身造成的,则可能无法选择容器来解决您的问题(作为测试,尝试将所有数据加载到数组中;如果内存不足,则需要另一种解决方案)。不仅如此,如果您将其削减到接近容量的水平,如果将来遇到大量记录,您仍然会遇到问题。
除了添加更多 RAM 之外,您还可以采用许多其他方法,但总体思路是在磁盘上存储更多,在内存上存储更少。以下是一些可能的替代方案:
- 将您的记录存储在适当的数据库中(这里有很多选项,SQLite可能是最方便的 - 也有许多访问选项,从直接
java.sql.*
到Hibernate)。 - 使用像MapDB这样的东西,正如Andrey Chaschev提到的。
- 如果程序经常访问一小部分数据,或者连续访问相同的数据,请考虑将记录保留在磁盘上,在需要时查找它们,并在找到时缓存它们(仅当感兴趣的记录不在缓存中时才在磁盘上搜索)。 与其将整个记录存储在地图中,
- 不如存储一些信息,以帮助您更快地在磁盘上找到它们,并根据需要延迟加载记录(例如,将记录数据的文件偏移量存储在地图中,然后在查找时,从文件中加载实际记录数据,如果需要,实现缓存)。
就个人而言,我会选择第一个选项(确保在通常用于查找记录的键上创建索引),因为它的设置和使用非常简单,并且SQLite(例如)是独立的,不需要服务器。以增加开发复杂性为代价,如果您发现性能要求未得到满足,您仍然可以缓存数据,或者像 Hibernate 这样的东西会为您完成此操作。
来自 javadoc。
This implementation provides guaranteed log(n) time cost for
the containsKey, get, put and remove operations.
因此,请使用树状图并为Java提供更多内存。
如果您有更多的基础设施支持,请尝试将内存增加到 4 或 5 GB,并使用这些映射中的任何一个
-
使用树状图 - 如果您希望对对象进行排序。由于对象已排序,因此在插入新对象后,对整个地图进行排序需要额外的时间。
-
使用哈希映射 - 用于快速添加/检索,因为对象未排序。