适用环境:文件过大,内存限制为一次只能读取部分数据
本例是个简单的外排序,相对排序效率过低,10000条数据排序时间在1000ms左右,仅作学习理解用
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* <p>
* 外排序算法 适用环境:文件过大,内存一次性不能装下
* </p>
*
* @author Administrator
*
*/
public class OutSort {
public static void main(String[] args) throws Exception {
File f = new File("src/input.data");
File tempFile = new File("src/temp.data");
if (!tempFile.exists()) {
tempFile.createNewFile();
}
File destFile = new File("src/dest.data");
if (!destFile.exists()) {
destFile.createNewFile();
}
outSort(f, tempFile, destFile, 50 , 25);
}
/**
* <p>
* 外排序,源文件中一行为个排序对象 读取指定行数的文件,放入数组/集合中进行排序,然后输出到临时文件中
* </p>
*
* @param sourceFile
* 源文件
* @param tempFile
* 临时文件
* @param destFile
* 结果文件
* @param pageSize
* 读取页面大小(可以理解成把原始文件分成若干个子文件)
* @param num
* 从临时子文件中加载的对象个数
* @throws Exception
*/
public static void outSort(File sourceFile, File tempFile, File destFile,
int pageSize,int num) throws Exception {
BufferedReader bufferedReader = new BufferedReader(new FileReader(
sourceFile));
// 将原文件分段排序后将记录每个分段信息的数据保存下来
List<OutSort.SortFile> sortfiles = new ArrayList<OutSort.SortFile>();
List<String> list = new ArrayList<String>();
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(
tempFile));
// 已经读取的记录行数
long size = 0;
// 已经读取的总行数
long hasedReadLine = 0;
// 已经读取的总字节数
long bytesize = 0;
// 第几个分段文件
int fileNum = 0;
// 要跳过的字节数
long skipByte = 0;
while (bufferedReader.ready()) {
// 读取一行
String s = bufferedReader.readLine();
list.add(s);
size++;
hasedReadLine++;
if (size == pageSize) {
// 生成分段对象,并保存分段信息
SortFile file = new SortFile(fileNum * pageSize + 1,
hasedReadLine, skipByte, num);
sortfiles.add(file);
fileNum++;
// 排序
Collections.sort(list);
for (String str : list) {
str = str + "\n";
bytesize += str.getBytes().length;
bufferedWriter.write(str);
}
// 要跳过的字节数
skipByte = bytesize;
// 下入文件
bufferedWriter.flush();
size = 0;
// 清除分段内容,以便下一个分段使用
list.clear();
}
}
// 余下不满一页的分段数据
if (size < pageSize && size > 0) {
SortFile file = new SortFile(fileNum * pageSize + 1, hasedReadLine,
skipByte, num);
sortfiles.add(file);
Collections.sort(list);
for (String str : list) {
bufferedWriter.write(str + "\n");
}
bufferedWriter.flush();
}
bufferedWriter.close();
// 从分开子文件对象中分别取出1个进行排序,将最小的写入结果文件,并在指定文件再读取一条记录
BufferedWriter writer = new BufferedWriter(new FileWriter(destFile));
for (SortFile sort : sortfiles) {
// 初始化读到内存中的数据
sort.readLine(tempFile);
}
// 初始化排序中间集合
List<SortObject> resultSort = new ArrayList<SortObject>();
for (SortFile sort : sortfiles) {
resultSort.add(sort.removeElement(tempFile));
}
long begin = System.currentTimeMillis();
System.out.println(begin);
// 排序并写入目标文件
while (hasedReadLine > 0) {
// 排序
Collections.sort(resultSort);
// 写入第一个元素
SortObject sortObject = resultSort.remove(0);
writer.write(sortObject.getValue() + "\n");
hasedReadLine--;
if (hasedReadLine % 1000 == 0) {
writer.flush();
}
// 移除子文件对象中对应数据
sortObject = sortObject.getKey().removeElement(tempFile);
if (sortObject != null) {
resultSort.add(sortObject);
}
}
writer.flush();
writer.close();
long end = System.currentTimeMillis();
System.out.println(end - begin);
}
private static class SortObject implements Comparable<SortObject> {
// 值
private final String value;
// 键
private final SortFile key;
public SortObject(SortFile key, String value) {
this.key = key;
this.value = value;
}
public SortFile getKey() {
return key;
}
public String getValue() {
return value;
}
public int compareTo(SortObject o) {
return value.compareTo(o.getValue());
}
}
private static class SortFile {
// 开始读取位置指针
private long startCursor;
// 结束位置指针
private final long endCursor;
// 要跳过的字节数
private long byteSize;
// 每次读取到内存中的元素个数
private final int num;
// 读取到内存中元素的集合
private final List<SortObject> sortObjects;
/**
* @param startCursor
* 开始指针位置
* @param endCursor
* 结束指针位置
* @param byteSize
* 要跳过的字节数
* @param num
* 每次读取到内存中的元素个数
*/
public SortFile(long startCursor, long endCursor, long byteSize, int num) {
this.endCursor = endCursor;
this.startCursor = startCursor;
this.byteSize = byteSize;
this.num = num;
sortObjects = new ArrayList<OutSort.SortObject>();
}
/**
* 移除元素,如果是最后一个,则填充
*
* @throws Exception
*/
public SortObject removeElement(File tempFile) throws Exception {
SortObject object = null;
if (sortObjects.size()>0 && sortObjects.get(0) != null) {
object = sortObjects.remove(0);
if (sortObjects.size() == 0) {
readLine(tempFile);
}
}
return object;
}
// 读取下一行数据
public List<SortObject> readLine(File tempFile) throws Exception {
if (startCursor > endCursor) {
return null;
}
BufferedReader reader = new BufferedReader(new FileReader(tempFile));
// 清空读取到内存中的数据,理论上已经清空了
sortObjects.clear();
// 跳过指定的字节数
reader.skip(byteSize);
for (int i = 0; i < num; i++) {
if (startCursor <= endCursor) {
String str = reader.readLine();
SortObject object = new SortObject(this, str);
sortObjects.add(object);
byteSize += (str + "\n").getBytes().length;
++startCursor;
}
}
return sortObjects;
}
}
}