将巨大的整数文件(在一行中)拆分为具有内存限制的排序块

Split huge file of integers (in one line) into sorted chunks with memory restriction

我最近需要将 一行文件(用“,”分隔的整数)排序​​为更小的块,同时考虑到内存限制和效率。我目前遵循以下逻辑:

File file = new File("bigfile.txt");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
int BUFFER_SIZE = 10; // can and should be bigger
byte[] bytes = new byte[BUFFER_SIZE];
while ((bis.read(bytes)) != -1) {
   // convert bytes to string
   // split bytes to String[]
   // save the last number if was cut in the middle and save it for the next round of reading and remove it from the current String[]
   // fix cut number if necessary and put it in the String[]
   // sort the String[]
   // write the String[] into a file
   // call Garbage collector to prevent memory leak?
}
bis.close();

假设我受限于 5MB 内存并且必须读取一个单行文件,其中包含 10,000,000 个由“,”分隔的整数:

对我来说,获得最少数量的排序文件(或每个文件可能的最大数据块)的最佳方法是什么?

这项任务并不容易。我确定这不是最好的方法,但总比没有好:

  1. 使用 sizecomparator 构造函数参数查找或创建一个 list,例如 PriorityQueue。应该知道尺寸(根据您的要求)
    • 方法 add(..) 应匹配 O(log n) 并在插入项目时对其进行排序
    • 方法 add(..) 应该 return false 当列表填满时(否则 true
  2. 执行流读取并立即(无缓冲)将整数添加到您的集合中。如果没有空闲则创建一个新列表 space.
  3. 现在您有了一组排序列表。最后一步是通过列表对数据进行排序: [1,4,5],[3,8,9],[2,6,7] --> [1,2,3], [4,5,6], [7,8,9] 例如,从 list#1 和 list#2 中选择最小值,比较它们,等等。 . .

注意:您也可以同时执行步骤#3

关于#2: 我错过了你有字符串数据。所以将字节序列解析为整数是个坏主意。但是,应该可以逐个字符地解析数据,然后在出现逗号时转换为 int。此外,还可以计算缓冲区大小(最大数字长度 * 每个符号的字节数)-> 对于 UTF-8 中的 2147483647,,它是 11 * 1。

我相信你可以应用Two-Pass Multiway Mergesort (TPMMS)解决问题。

我将向您提供有关您可以做什么的一般概念,但是,如果您阅读更多有关 TPMMS 的内容会更好:

// 每次读取一个块时,必须确保没有遗漏任何数字(如果最后一位是数字,请继续一点一点地读取,直到遇到“,”)

  • 在有限的 RAM 和遵循 TPPMS 的情况下,您必须将文件分成多个块,对其进行排序,然后将每个块保存到单独的文件中。
  • 对于每个文件,创建一个PriorityQueue并读取一定数量的字节(这样你就可以读取所有的小文件)并将它们转换回数字存储在队列中。为方便起见,我将此 PriorityQueues 列表称为 pqs
  • 创建另一个 PriorityQueue (pq),其大小等于您拥有的小文件的数量,并推送每个 pqs[= 队列的第一个值41=].
  • 有趣的部分来了;因为您使用的是 PriorityQueue (pq) 并且您在 pqs 中弹出了每个 PriorityQueue 的第一个值,所以保证 [=] 中的第一个值22=]pq是最小值。 (对于在 pq 中弹出的每个元素,您可以将其直接写入最终文件,也可以将其保存在数组中并在数组时将其写入最终文件已满,我更喜欢最后一个选项。)
  • 每次弹出 pq 时,您都必须从获取该值的文件中读取下一个数字,将其放入正确的 PriorityQueue 中pqs 然后弹出该 PriorityQueue 的第一个值。
  • 你重复上一步,直到pqs中的所有PriorityQueue都为空。

由于内存量有限,您将不得不调整每个缓冲区的大小。