我的文件很大,每行都有独特的单词。文件大小约为1.6 GB(我要在此之后对其他文件进行排序,大约为15GB)。直到现在,对于我使用Array.sort()
的较小文件。但对于这个文件,我得到java.lang.OutOfMemoryError: Java heap space
。我知道这个错误的原因。有什么办法,而不是编写完整的快速排序或合并排序程序。
我读到Array.sort()在内部使用Quicksort或Hybrid Sort。有没有像Array.sort()
??
如果我必须编写一个程序进行排序,我应该使用哪一个? Quicksort或Merge排序。我担心最坏的情况。
答案 0 :(得分:6)
根据要存储的数据的结构,您可以做很多不同的事情。
如果结构良好的数据需要按一个或多个特定字段排序(在这种情况下系统工具可能没有用),那么最好使用允许排序的数据存储区。考虑到尺寸不超过几百GB,我认为MongoDB非常适合这种情况。其他NoSQL数据存储也可能很好地适应这个法案,尽管Mongo的使用和安装简单以及对JSON数据的支持使它成为一个非常好的候选者。
如果你真的想要使用java方法,它会变得非常棘手。这是你在求职面试时提出的问题,我实际上也不会指望任何人实现代码。但是,一般的解决方案是合并排序(使用随机访问文件是一个坏主意,因为它意味着插入排序,即非最佳运行时间,考虑到文件的大小,这可能是不好的)。
通过合并排序我的意思是在足够小的时间读取文件的一个块以使其适合内存(因此它取决于你有多少RAM),对其进行排序然后将其写回磁盘上的新文件。读完整个文件后,您可以通过只读取每个文件的头部并将两个文件中较小的文件(两个记录中较小的一个)写回第三个文件,一次开始合并两个块文件。为“第一代”文件执行此操作,然后继续使用第二代文件,直到最终得到一个大的已排序文件。请注意,这基本上是实现合并排序的自下而上的方式,学术递归算法是自上而下的方法。
请注意,使用multiway merge algorithm可以完全避免使用中间文件。这通常基于堆/优先级队列,因此实现可能稍微复杂一些,但它会减少所需的I / O操作数。
使用一些精心设计在java中实现上述内容应该不会太困难,尽管它肯定会变得棘手。我仍然强烈推荐像Mongo这样开箱即用的解决方案。
答案 1 :(得分:0)
事实证明,你的问题是你的堆不能容纳这么大的数组,所以你必须忘记任何暗示将整个文件内容加载到数组中的解决方案(只要你不能扩展你的堆)。
所以你面对流媒体。当您必须处理大于可用内存的输入源时,这是唯一(也是典型的)解决方案。我建议将文件内容流式传输到您的程序,该程序应该通过输出到随机访问文件(棘手的)或数据库来执行排序。
答案 2 :(得分:0)
我会采取不同的方法。
给定一个文件,比如每行一个元素,我会读取第一个n
元素。我会重复此m
次,以使文件中的行数为n * m + C
,C
为剩余行。
在处理Integers
时,您可能希望每次阅读使用大约100,000个元素,Strings
我会使用更少,可能大约1,000。它取决于每个元素所需的数据类型和内存。
从那里开始,我会对n
个元素进行排序,并将它们写入具有唯一名称的临时文件中。
现在,由于您已对所有文件进行了排序,因此最小的元素将位于开头。然后,您可以迭代文件,直到处理完所有元素,找到最小元素并将其打印到新的最终输出。
此方法将减少所需的RAM量,而是依赖于驱动器空间,并允许您处理任何文件大小的排序。
答案 3 :(得分:-1)
在文件中构建记录位置数组(索引类型),也许它会适合内存。每个文件记录需要一个8字节的java long
。对数组进行排序,仅加载记录以进行比较而不保留(使用RandomAccessFile
)。排序后,使用索引指针编写新的最终文件,以按所需顺序获取记录。
如果记录的大小不同,这也会有效。