背景
这是一道面试题,可考察的点也不少。总结几个关键词去解决这个问题,1,文件拆分;2、排序算法;3、缓冲buffer性能优化。
啊,乍一看,这绝对不是一个初级程序员能够答出来,且能答得很好的问题,这个题目可以考察到我们的算法能力,性能优化经验。可万万不能马虎对待!
开始讲思路。第一步,
文件拆分
首先外部输入的文件,如果不出意外的话大文件,我们都不建议一次性地将其加载到我们的内存中,内存可是很宝贵的,它还要干其他很多很多的事情。不能因为一个文件的导入,而让内存陷入卡顿的危机。所以,针对这种大文件,我们首先就是给他做个拆分,即批量处理嘛,因为本质是给数字进行排序,那我就将这个大文件拆分为15个小文件。
然后依次将这些文件加载到内存中,经过我们的排序算法(可以是快速排序,堆排序,归并排序,等,当然你非要用冒泡我觉得也可以,不过大家应该还是比较喜欢快一点的排序算法), 输出一个新的有序文件。这样一通操作下来,我们就有个15个新的已经是有序的文件了。但是,我们最终需要输出一个大文件,大小还是10GB的有序文件。
所以,来到第二步。
多路归并
多路归并的意思就是说,将这个几个有序的文件中的数字,全部归在一起,做一个整体的排序。然后输出一个有序的大文件。
想必大家也听说过归并排序。那这里可以直接拿来用吗?不好意思,用不了。归并是针对两个数组,每次拿出数组中的两个元素进行比较,那如果是三个数组的话,不就不好比较了。
有一种办法是先取出两个文件进行二路归并,然后再将输出的有序文件和第三个无序文件进行归并,这样我们归并到后面,数字越来越多,有占满内存的风险。
本博文将讨论一种比较好的多路归并办法,也是通用的技术,就是使用堆排序。
针对15个有序文件,先新建一个最小堆,然后取这个15个文件中的第一个元素进行初始化。一定要一层一层取,这样不会乱序。
堆内会自动排序,最小堆的堆顶放置的是最小的元素。
然后,取出堆顶元素,使用一个数据结构list存放起来。接着,我们依次处理剩余文件中的数字,遍历15个文件,每次从文件中取出一个数字,放入堆中,此时堆内自动排序,就会将最小元素放置在堆顶, 我们移除堆顶元素放入list中。这样子,一个一个处理,最终遍历结束以后,List 中就会得到一个有序的数字集合。
最终我们将List中的数字输出到文件持久化存储就好了。
关于堆排序的过程,可以见其他博客。
性能优化
仔细看多路归并这个过程,发现list存储10GB的数字会不会有点太大了呢?而且,每次从文件中取数字,一层一层取,A文件第二个,B文件第二个,C文件第二个....A文件第三个,B文件第三个,C文件第三个, 这样与磁盘的交互是不是有点太频繁了。这个时候,考虑使用缓冲区优化吧,不然太消耗IO了。
放大招,使用Buffer。由于我们处理的是整型数字,所以可选择IntBuffer,容量可设置为8KB,这个根据需要处理的数据量来定。
这样,多路归并的过程就可以是,遍历文件,每次从各个文件中取出一个元素,放入buffer。取个几层以后,buffer要满了,我们再将buffer中的数据拿去做堆排序。
然后清空buffer, 继续遍历文件,取元素,重复以上过程。
同样的,对于List中这个数据结构,我们也使用一个输出buffer去接收已排序的元素。等buffer满了,将数据flush到输出文件中。
以上就是这个问题的整体思路啦。还有什么要补充的, 欢迎打在评论区。