小巧易于使用的有磁盘支持的ArrayList -Bozho的技术博客


有时,列表List可能会变得太大而无法容纳在内存中,因此您必须采取一些措施以避免内存不足。
做到这一点的正确方法是流传输–而不是将所有内容都放入内存中,您应该从源流传输数据并丢弃已经处理的条目。
但是,在某些情况下,超出代码控制一个列表范围,但是不能使用流式传输。这些情况很少见,但万一碰到,您必须找到解决方法。一种是重新实现代码以与流一起使用,但是根据编写库的方式,可能无法实现。因此,另一种选择是使用磁盘支持的列表。
搜索现有的解决方案会产生3年以上的库包,例如这个这个这个
然后是MapDB,它很棒并且受支持。它主要是关于Map的,但是它也支持List,如此处所示

最后,如果您只需要迭代而几乎不需要别的,则可以选择自己实现一些简单的事情。我在这里做了– DiskBackedArrayList.java
它不支持上述库包的很多功能,但最重要的是,它不支持随机添加和随机获取,也不支持toArray()。它纯粹是“填充列表”,然后“迭代列表”。它所依赖的ObjectOutputStream并不是十分高效,而是易于使用。请注意,在需要将少量数据添加到列表的情况下,我允许使用较短的内存prependList。
该列表将填充到内存中,直到达到指定的阈值,然后刷新到磁盘,清除内存,内存又开始被填充。这也可能会更有效–在另一个线程中进行后台刷新,这不会干扰将元素添加到列表中,但是优化使事情变得复杂,在这种情况下,总运行时间不是问题。
最重要的是,该iterator()方法被覆盖以返回一个自定义迭代器,该迭代器首先流式传输前置列表,然后从磁盘读取所有内容,最后遍历仍在内存中的最新批处理。最后,clear()应该最后调用该方法以关闭基础流。可以在每次刷新时打开和关闭输出流,但是ObjectOutputStream 由于某些特定于首先写入标头的实现,因此不能在附加模式下使用。

因此,基本上,我们将流方法隐藏在List接口下–它仍然是流元素,不需要时将其丢弃。理想情况下,应在数据源(例如数据库,消息队列等)上完成此操作,而不是将磁盘用作溢出空间,但是在某些情况下,使用磁盘很好。此实现是一个起点,因为尚未在生产中进行测试,但是说明您可以根据需要使现有类适应不同的数据访问模式。