有时,您的列表可能会变得太大而无法容纳在内存中,因此您必须采取一些措施以避免内存不足。
做到这一点的正确方法是流传输–而不是将所有内容都放入内存中,您应该从源流传输数据并丢弃已经处理的条目。
但是,在某些情况下,您无法控制的代码需要一个List
而您不能使用流式传输。 这些情况很少见,但万一击中它们,您必须找到解决方法。 一种是重新实现代码以与流一起使用,但是根据编写库的方式,可能无法实现。 因此,另一种选择是使用磁盘支持的列表–一个用作列表的列表,但在其下方存储和加载磁盘中的元素。
搜索现有的解决方案会产生3年以上的仓库,例如这个 , 这个和这个 。
然后是MapDB ,它很棒并且受支持。 它主要是关于地图的,但是它也支持List,如此处所示 。
最后,如果您只需要迭代而几乎不需要别的,则可以选择自己实现一些简单的事情。 我在这里做了– DiskBackedArrayList.java 。 它不支持很多东西(并非所有方法都被重写以引发异常,但是应该)。 但最重要的是,它不支持随机添加和随机获取,也不支持toArray()。 它纯粹是“填充收藏集”,然后“迭代收藏集”。 它依赖于ObjectOutputStream
,效率不是很高,但是易于使用。 请注意,在需要将少量数据添加到列表的情况下,我允许使用较短的内存prependList。
该列表将填充到内存中,直到达到指定的阈值,然后刷新到磁盘,清除内存,内存又开始被填充。 这也可能会更有效–在另一个线程中进行后台刷新,这不会干扰将元素添加到列表中,但是优化使事情变得复杂,在这种情况下,总运行时间不是问题。 最重要的是,重写iterator()
方法以返回一个自定义迭代器,该迭代器首先流式传输前置列表,然后从磁盘读取所有内容,最后遍历仍在内存中的最新批处理。 最后,最后应调用clear()
方法以关闭基础流。 可以在每次刷新时打开和关闭输出流,但是由于某些特定于首先写入标头的实现,因此不能在附加模式下使用ObjectOutputStream
。
因此,基本上,我们将流方法隐藏在List
接口的下面–它仍在流元素并在不需要时将其丢弃。 理想情况下,应在数据源(例如数据库,消息队列等)上完成此操作,而不是将磁盘用作溢出空间,但是在某些情况下,使用磁盘很好。 此实现是一个起点,因为尚未在生产中进行测试,但是说明您可以根据需要使现有类适应不同的数据访问模式。
翻译自: https://www.javacodegeeks.com/2019/12/a-disk-backed-arraylist.html