一、問題描述
有一個大文件,里面有十億個字符串,亂序的,要求將這些字符串以字典的順序排好序
?
二、解決思路
? ? 將大文件切割成小文件,每個小文件內歸并排序;
? ? 對所有的小文件進行歸并排序——多重歸并排序
?
三、解決方案
3.1?模擬產生10億個隨機字符
public static void generateDate() throws IOException {BufferedWriter writer = new BufferedWriter(new FileWriter(ORIGINALPATH));Random random = new Random();StringBuffer buffer = new StringBuffer("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");int range = buffer.length();int length = 1;for (int i = 0; i < BIGDATALENGTH; i++) {StringBuffer sb = new StringBuffer();length = random.nextInt(20)+1;//System.out.println("length--->"+length);for (int j = 0; j < length; j++) {//System.out.println("j--->"+j);sb.append(buffer.charAt(random.nextInt(range)));}System.out.println("sb---->"+sb);writer.write(sb.toString() + "
");}writer.close();
}
?
3.2?對大文件進行切割
/**
}
/*** 將原始數據分成幾塊 并排序 再保存到臨時文件* @throws IOException*/
public static void splitData() throws IOException {@SuppressWarnings("resource")BufferedReader br = new BufferedReader(new FileReader(ORIGINALPATH));tempFiles = new File[BIGDATALENGTH / TEMPFILELENGTH];//將會產生的臨時文件列表for (int i = 0; i < tempFiles.length; i++) {tempFiles[i] = new File(TEMPFILEPATH + "TempFile" + i + ".txt");BufferedWriter writer = new BufferedWriter(new FileWriter(tempFiles[i]));HashMap<Integer,String> hashMap = new HashMap<Integer,String>();//未排序//每次讀出TEMPFILELENGTH個文件 保存到smallLine中for (int j = 1; j <= TEMPFILELENGTH; j++) {String text = null;if ((text = br.readLine()) != null) {hashMap.put(j, text);}}hashMap = MergeSort.sort(hashMap);for(int k=1; k<=TEMPFILELENGTH; k++){writer.write(String.valueOf(hashMap.get(k))+ System.getProperty("line.separator"));
//System.getProperty("line.separator")相當于}writer.close();}
}
?
3.3?對小文件進行遞歸歸并
?
/*** 多路歸并排序* @param files* @throws IOException*/
public static void multiWaysMergeSort(String[] files) throws IOException {System.out.println("歸并文件-----第 "+mergeSortCount+" 次-----");//當最后只有一個文件的時候 數據已經排序成功 直接復制保存到結果文件if (files.length == 1) {String lastFilePath = LASTFILEPATH + LASTFILENAME;copyFile(files[0], lastFilePath, false);//deleteFile(files[0]);return;}for (int i = 0; i < files.length; i+=2) {
//開始合并兩個相鄰的文件 所以一次跳兩個if (i == files.length - 1) {
//這時候已經只剩下最后一個文件了 不需要合并 本趟歸并結束renameFile(files[i], i);break;}//將br1 和 br2 寫入到WriteBufferedReader br1 = new BufferedReader(new FileReader(files[i]));BufferedReader br2 = new BufferedReader(new FileReader(files[i + 1]));BufferedWriter writer = new BufferedWriter(new FileWriter(TEMPFILEPATH + "last_" + mergeSortCount + "_" + i + ".txt"));String s1 = br1.readLine();String s2 = br2.readLine();while (s1 != null || s2 != null) {if (s1 != null && s2 != null) {
//都不為空 才有比較的必要int mergeResult = s1.compareTo(s2);if (mergeResult > 0) {//s1在s2后面writer.write(s2);writer.write(System.getProperty("line.separator"));s2 = br2.readLine();}if (mergeResult == 0) {//s1=s2writer.write(s1); writer.write(System.getProperty("line.separator"));writer.write(s2); writer.write(System.getProperty("line.separator"));
// System.out.println("write time : " + writeTime++);s1 = br1.readLine();s2 = br2.readLine();}if (mergeResult < 0) {//s1在s2前面writer.write(s1); writer.write(System.getProperty("line.separator"));s1 = br1.readLine();}}if (s1 == null && s2 != null) {writer.write(s2);writer.write(System.getProperty("line.separator"));s2 = br2.readLine();}if (s2 == null && s1 != null) {writer.write(s1);writer.write(System.getProperty("line.separator"));s1 = br1.readLine();}}br1.close();br2.close();
// deleteFile(files[i]);
// deleteFile(files[i + 1]);writer.close();}mergeSortCount++;multiWaysMergeSort(getTempFiles("last_" + (mergeSortCount-1) + "_"));
}
?
3.4?運行結果分析
①生成10億個隨機字符串,時間太久了,,字符串長度隨機在[1,20]之間時,文件大小大概在10.7?GB?(11,500,161,591?字節)
②?切割成小文件,小文件內歸并排序,每個文件內的數據100萬條時,隨機選取五個排序時間如下:
一共發生了410832612?次對比一共發生了?899862656?次交換執行時間為3545毫秒
一共發生了429506513?次對比一共發生了?940765504?次交換執行時間為3512毫秒
一共發生了448181315?次對比一共發生了?981668352?次交換執行時間為3497毫秒
一共發生了466856137?次對比一共發生了?1022571200?次交換執行時間為3497毫秒
一共發生了485530473?次對比一共發生了?1063474048?次交換執行時間為3981毫秒
總共1000個文件切割耗時為
切割小文件所用時間--->4341734ms--->4341.734s--->72.36m--->1.206h
③??小文件遞歸歸并,1000個文件,
共發生了10次歸并,
產生臨時文件總共1999個,
總大小為127.8?GB?(137,201,789,278?字節),
產生結果文件11.6?GB?(12,500,161,591?字節)
比源文件多了10億個字節......
總耗時為--->7374129ms--->7374.129s--->122.9m--->2.048h
不得不提的是,最后執行結果成功,也不枉我苦苦等待
四、相關技術
4.1?歸并排序
排序原理不多介紹,各種到處都有,如果一時不記得,看下面的原理圖。秒懂。
??
4.2?文件讀寫
本程序很重要的一點就是對于文件的讀寫,Buffer的文件讀寫可以很大程度的改善速率
寫操作:
BufferedWriter?writer?=?new?BufferedWriter(new?FileWriter(PATH));
writer.write("hhf ");
讀操作:
BufferedReader?br?=?new?BufferedReader(new?FileReader(PATH));
text?=?br.readLine()
?
五、關于優化
5.1分小文件時優化
前提:數據均勻,保證每個小文件大小不會超過內存的容量
處理:在分數據到小文件時,按字符串按首字母將其分到指定文件中,如A-C分配到1.txt,D-F分配到2.txt.......
優點:只需要小文件內數據排序,排序號后,即可將1.txt、2.txt、3.txt直接連接起來,極大的縮短了歸并時間,相當于把遞歸歸并變成了文件連接而已
缺點:前提不是很容易把握,若有一個小文件內的數據量大于內存的大小,則排序失敗,存在一定的風險
5.2小文件內排序時優化
前提:保證每個小文件內數據量比較不是特別的大
處理:將小文件內的數據進行快速排序
優點:快排的時間效率是高于歸并的
以下是測試數據
排序數量級??101000100000
歸并排序7ms71ms3331ms
快速排序6ms52msjava.lang.StackOverflowError
缺點:缺點已經顯示在測試數據內了,小文件內的數據量過大就可能導致當前線程的棧滿
原文鏈接