将巨大的整数文件(在一行中)拆分为具有内存限制的排序块
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 个由“,”分隔的整数:
- 如果我使用非常小的缓冲区大小(例如 10)来读取文件,那么我将
创建数千个文件。
- 如果我使用合适但仍然很小的缓冲区大小(例如 100KB),那么我会
仍然得到很多文件。
- 如果我使用更大的缓冲区大小(例如 4MB),那么我将拥有堆
由于限制,在内存中对结果进行排序和拆分时出现问题。
对我来说,获得最少数量的排序文件(或每个文件可能的最大数据块)的最佳方法是什么?
这项任务并不容易。我确定这不是最好的方法,但总比没有好:
- 使用
size
和 comparator
构造函数参数查找或创建一个 list
,例如 PriorityQueue
。应该知道尺寸(根据您的要求)
- 方法
add(..)
应匹配 O(log n)
并在插入项目时对其进行排序
- 方法
add(..)
应该 return false
当列表填满时(否则 true
)
- 执行流读取并
立即(无缓冲)将整数添加到您的集合中。如果没有空闲则创建一个新列表 space.
- 现在您有了一组排序列表。最后一步是通过列表对数据进行排序:
[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都为空。
由于内存量有限,您将不得不调整每个缓冲区的大小。
我最近需要将 一行文件(用“,”分隔的整数)排序为更小的块,同时考虑到内存限制和效率。我目前遵循以下逻辑:
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 个由“,”分隔的整数:
- 如果我使用非常小的缓冲区大小(例如 10)来读取文件,那么我将 创建数千个文件。
- 如果我使用合适但仍然很小的缓冲区大小(例如 100KB),那么我会 仍然得到很多文件。
- 如果我使用更大的缓冲区大小(例如 4MB),那么我将拥有堆 由于限制,在内存中对结果进行排序和拆分时出现问题。
对我来说,获得最少数量的排序文件(或每个文件可能的最大数据块)的最佳方法是什么?
这项任务并不容易。我确定这不是最好的方法,但总比没有好:
- 使用
size
和comparator
构造函数参数查找或创建一个list
,例如PriorityQueue
。应该知道尺寸(根据您的要求)- 方法
add(..)
应匹配O(log n)
并在插入项目时对其进行排序 - 方法
add(..)
应该 returnfalse
当列表填满时(否则true
)
- 方法
- 执行流读取并
立即(无缓冲)将整数添加到您的集合中。如果没有空闲则创建一个新列表 space. - 现在您有了一组排序列表。最后一步是通过列表对数据进行排序:
[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都为空。
由于内存量有限,您将不得不调整每个缓冲区的大小。