我有一个 2GB 大小的文件,里面有学生记录。我需要根据每条记录中的某些属性找到学生,并创建一个包含结果的新文件。过滤学生的顺序应与原始文件中的顺序相同。在没有内存问题的情况下,使用 Java IO API 和线程执行此操作的最有效和最快的方法是什么?JVM 的最大堆大小设置为 512MB。
4 回答
什么样的文件?基于文本,如 CSV?
最简单的方法是执行类似 grep 的操作:逐行读取文件,解析该行,检查您的过滤条件,如果匹配,则输出结果行,然后转到下一行,直到文件完成。这是非常节省内存的,因为您只能同时加载当前行(或稍大一点的缓冲区)。您的进程只需要通读整个文件一次。
我认为多个线程不会有太大帮助。这会使事情变得更加复杂,并且由于进程似乎无论如何都是 I/O 绑定的,因此尝试使用多个线程读取同一个文件可能不会提高吞吐量。
如果你发现你需要经常这样做,并且每次浏览文件太慢,你需要建立某种索引。最简单的方法是首先将文件导入数据库(可以是嵌入式数据库,如 SQLite 或 HSQL)。
在您发现无聊的简单方法无法满足您的需求之前,我不会使这变得过于复杂。基本上你只需要:
- 打开输入流到 2GB 文件,记住缓冲(例如通过 BufferedInputStream 包装)
- 打开输出流到您要创建的过滤文件
- 从输入流中读取第一条记录,查看任何属性以决定是否“需要”它;如果这样做,请将其写入输出文件
- 重复剩余记录
在我的一个硬件极其适中的测试系统上,开箱即用的 FileInputStream 周围的 BufferedInputStream 在 25 秒内读取了大约 500 MB,即处理 2GB 文件可能不到 2 分钟,并且默认缓冲区大小基本上和它一样好(有关更多详细信息,请参阅我制作的BufferedInputStream 计时)。我想用最先进的硬件很可能时间会减半。
无论您是需要付出很多努力来减少 2/3 分钟,还是只是在等待它运行时稍作休息,这都是您必须根据自己的要求做出的决定。我认为数据库选项不会给您带来太多收益,除非您需要对同一组数据进行大量不同的处理(并且还有其他解决方案并不意味着数据库)。
- 2GB 的文件是巨大的,你应该去一个 db。
- 如果你真的想使用Java I/O API,那么试试这个:Handling large data files Effective with Java和这个:Tuning Java I/O Performance
我认为您应该使用内存映射文件。这将帮助您将较大的文件映射到较小的内存。这将像虚拟内存一样,就性能而言,映射文件比流写入/读取更快。