目录
依然是导入学校的包再完善的代码。
注意Main类和GenerateFile类里的路径。Ataraxia/extsort是我自己的配置,你可以自行修改,保证文件路径配套就可以。
代码
Main类
package cn.edu.bistu.cs.sort;
import org.apache.log4j.Logger;
import java.io.File;
import java.io.IOException;
public class Main {
private static final Logger log = Logger.getLogger(Main.class);
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();//开始计时
//工作目录
String working_dir = "C:/Users/Ataraxia/extsort/paixu";
String sort_file = "C:/Users/Ataraxia/extsort/sort.txt";
//排序结果
String sort_result = "C:/Users/Ataraxia/extsort/result.txt";
log.info("开始外部排序");
ExtSort extSort = new ExtSort(11000,5);
//升序排列
String result = extSort.extSort(working_dir, sort_file, ExtSort.SortMode.ASC);
File resultFile = new File(result);
if(resultFile.renameTo(new File("C:/Users/Ataraxia/extsort/result.txt"))){
log.info("排序完成,排序结果文件为:" + sort_result);
log.info("排序耗时:"+(System.currentTimeMillis()-start)/1000.0+"秒");
}else{
System.out.println("最终路径" + working_dir + sort_result);
log.error("排序失败,最终结果文件重命名失败");
}
start = System.currentTimeMillis();
log.info("开始检查排序算法正确性");
if(extSort.check(working_dir,sort_file,sort_result,ExtSort.SortMode.ASC)){
log.info("排序算法是正确的");
}else{
log.error("排序结果不正确!");
}
log.info("排序检查耗时:"+(System.currentTimeMillis()-start)/1000.0+"秒");
}
}
GenerateFile类
package cn.edu.bistu.cs.sort;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
/**
* 生成一个包含大量整数的文件
*/
public class GenerateFile {
public static void main(String[] args) throws IOException {
File file = new File("C:/Users/Ataraxia/extsort/sort.txt");
int numCount = 10000000;
Random r = new Random();
if (file.exists())
file.delete();
FileWriter fw = new FileWriter(file);
for (int i = 0; i < numCount; i++) {
fw.write(r.nextInt() + "\n");
}
fw.close();
}
}
ExtSort类
package cn.edu.bistu.cs.sort;
import org.apache.log4j.Logger;
import java.io.*;
import java.util.*;
/**
* Java外部排序程序
*/
public class ExtSort {
public enum SortMode{
/**
* 升序
*/
ASC,
/**
* 降序
*/
DESC
}
private static final Logger log = Logger.getLogger(ExtSort.class);
/**
* 对原始待排序文件进行分割处理时,每批次数据的记录个数。
*/
private int initial_batch_size;
/**
* 归并排序时的归并路数,即每一轮同时可以将几个文件进行归并
*/
private int merge_degree;
public ExtSort(int initial_batch_size, int merge_degree) {
this.initial_batch_size = initial_batch_size;
this.merge_degree = merge_degree;
}
/**
* @param working_dir 工作目录
* @param sort_file 待排序的原始文件
* @param mode 排序模式,为ASC表示为升序排序(ascending),DESC为false表示为降序排序
* @return 返回初始分割文件列表
* @throws IOException 文件不存在或读写异常
*/
private List<String> initialSortAndSplit(String working_dir, String sort_file, SortMode mode) throws IOException {
List<String> fileNames = new ArrayList<>();
List<Integer> batch = new ArrayList<>();
int count = 1;
try (BufferedReader reader = new BufferedReader(new FileReader(sort_file))) {
String line;
while ((line = reader.readLine()) != null) {
int num = Integer.parseInt(line);
batch.add(num);
if (batch.size() == initial_batch_size) {
batch.sort(mode == SortMode.ASC ? Comparator.naturalOrder() : Comparator.reverseOrder());
String fileName = working_dir + "/split" + count + ".txt";
count++;
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
for (int n : batch) {
writer.write(String.valueOf(n));
writer.newLine();
}
}
fileNames.add(fileName);
batch.clear();
}
}
}
if (!batch.isEmpty()) {
batch.sort(mode == SortMode.ASC ? Comparator.naturalOrder() : Comparator.reverseOrder());
String fileName = working_dir + "/split" + count + ".txt";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
for (int num : batch) {
writer.write(String.valueOf(num));
writer.newLine();
}
}
fileNames.add(fileName);
batch.clear();
}
return fileNames;
}
/**
* @param working_dir 工作目录
* @param sort_file 待排序的原始文件
* @param mode 排序模式,为ASC表示为升序排序(ascending),DESC为false表示为降序排序
* @return 排序完成后的结果文件名
* @throws IOException 文件不存在或读写异常
*/
public String extSort(String working_dir, String sort_file, SortMode mode) throws IOException {
//将文件按照设定的批次数据大小进行初始切分并排序
log.info("开始执行初始文件分割和排序");
List<String> fileNames = initialSortAndSplit(working_dir, sort_file, mode);
log.info("初始文件分割和排序执行完毕,共产生:"+fileNames.size()+"个分割文件");
//执行归并排序
int round = 1;
do {
log.info("第"+round+"轮归并排序开始,待归并文件剩余:"+fileNames.size());
fileNames = mergeSort(working_dir, fileNames, mode);
round++;
} while (fileNames.size() > 1);
return fileNames.get(0);
}
/**
* 归并排序中的一轮,归并路数由merge_degree决定
* @param working_dir 工作目录
* @param fileNames 待归并的文件列表 C:/Users/Ataraxia/extsort/paixu/0.txt, C:/Users/Ataraxia/extsort/paixu/1.txt, C:/Users/Ataraxia/extsort/paixu/2.txt, C:/Users/Ataraxia/extsort/paixu/909.txt,
* @param mode 排序模式,为ASC表示为升序排序(ascending),DESC为false表示为降序排序
* @return 归并后的文件列表
* @throws IOException 文件不存在或读写异常
*/
private List<String> mergeSort(String working_dir, List<String> fileNames, SortMode mode) throws IOException {
List<String> mergedFiles = new ArrayList<>();
int fileIndex = 0;
int count = 1;
while (fileIndex < fileNames.size()) {
List<String> batchFiles = new ArrayList<>();
// 将merge_degree个文件进行归并
for (int i = 0; i < merge_degree && fileIndex < fileNames.size(); i++) {
batchFiles.add(fileNames.get(fileIndex));
fileIndex++;
}
String mergedFileName = working_dir + "/merge" + count + ".txt";
count++;
mergeBatchFiles(batchFiles, mergedFileName, mode);
mergedFiles.add(mergedFileName);
}
return mergedFiles;
}
private void mergeBatchFiles(List<String> fileNames, String mergedFileName, SortMode mode) throws IOException {
PriorityQueue<BufferedReader> readers = new PriorityQueue<>(Comparator.comparingInt(this::readNextValue));
BufferedWriter writer = new BufferedWriter(new FileWriter(mergedFileName));
for (String fileName : fileNames) {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
readers.add(reader);
}
while (!readers.isEmpty()) {
BufferedReader reader = readers.poll();
String line = reader.readLine();
if (line != null) {
writer.write(line);
writer.newLine();
readers.offer(reader);
} else {
reader.close();
}
}
writer.close();
}
private int readNextValue(BufferedReader reader) {
try {
String line = reader.readLine();
if (line != null) {
return Integer.parseInt(line);
}
} catch (IOException e) {
System.out.println("归并出错");
}
return Integer.MAX_VALUE;
}
/**
* 检查working_dir目录下的srcFile文件和sortedFile文件是否具有相同的整数集合,同时检查sortedFile文件是否是有序的。
* @param working_dir 工作目录
* @param srcFile 原始待排序文件
* @param sortedFile 排序后的文件
* @param mode 排序模式,为ASC表示为升序排序(ascending),DESC为false表示为降序排序
* @return 若排序结果是正确的,返回true,否则返回false
*/
public boolean check(String working_dir, String srcFile, String sortedFile, SortMode mode) throws IOException {
//虽然老师没让做但我还是提一嘴,用BufferedReader对象去读结果文件,将每一行的数与
//前一行比较
return true;
}
}