在java中加载大型文本文件的最佳方法

我有一个文本文件,每行有一个整数序列:

47202 1457 51821 59788 49330 98706 36031 16399 1465 ... 

该文件有300万行这种格式。 我必须将此文件加载到内存中并从中提取5-gram并对其进行一些统计。 我确实有内存限制(8GB RAM)。 我试图最小化我创建的对象的数量(只有1个类,包含6个浮点变量,以及一些方法)。 并且该文件的每一行基本上生成该类的对象数(与#ofwords中的行的大小成比例)。 当C ++出现时,我开始觉得Java不是一个很好的方法来做这些事情。

编辑:假设每一行产生该类的(n-1)个对象。 其中n是由空格分隔的该行中的标记数(即1457)。 因此,考虑到每行10个字的平均大小,每条线平均映射到9个对象。 因此,将有9 * 3 * 10 ^ 6个对象。所以,所需的内存是:9 * 3 * 10 ^ 6 *(8字节obj标头+ 6 x 4字节浮点数)+(一个地图(字符串,对象)和另一个映射(Integer,ArrayList(Objects)))。 我需要将所有内容保存在内存中,因为之后会发生一些数学优化。

读取/解析文件

在任何语言中处理大型文件的最佳方法是尝试将其加载到内存中。

在java中,看看MappedByteBuffer 。 它允许您将文件映射到进程内存并访问其内容,而无需将整个内容加载到堆中。

您也可以尝试逐行读取文件并在读取后丢弃每一行 – 再次避免将整个文件同时保存在内存中。

处理生成的对象

对于处理解析时生成的对象,有以下几种选择:

  1. 与文件本身相同 – 如果您可以执行任何想要执行的操作而不将所有内容保留在内存中(同时“流式传输”文件) – 这是最佳解决方案。 你没有描述你试图解决的问题,所以我不知道这是否可能。

  2. 压缩某种 – 从Wrapper对象(Float)切换到基元(float),使用类似flyweight模式的东西将数据存储在巨大的float []数组中,只构造短期对象来访问它,找到一些模式允许您更紧凑地存储数据的数据

  3. 缓存/卸载 – 如果您的数据仍然不适合内存“将其分页”到磁盘。 这可以简单到将guava扩展到页面到磁盘或引入像ehcache或类似的库。

特别是关于java集合和映射的注释

对于小型对象,特别是java集合和映射会导致大量内存损失(主要是由于所有内容都被包装为对象以及Map.Entry内部类实例的存在)。 如果内存消耗是一个问题,你可能应该看一下gnu trove集合。

最优的是只保持整数和行结束。

为此,一种方法是:将文件转换为两个文件:

  • 一个二进制文件的整数(4个字节)
  • 一个带索引的二进制文件,下一行将开始。

为此,可以使用Scanner进行读取,并使用DataOutputStream + BufferedOutputStream进行写入。

然后你可以在基本类型的数组中加载这两个文件:

 int[] integers = new int[(int)integersFile.length() / 4]; int[] lineEnds = new int[(int)lineEndsFile.length() / 4]; 

可以使用MappedByteBuffer.toIntBuffer()完成读取。 (你甚至不需要数组,但它会变得有点像COBOL那样冗长。)