将文件中所有数读到一个数组中_「数据结构与算法」将5个文件中的一千万年龄合并到一个新文件中...
現在有5個文件,文件里面分別存儲著1千萬個用戶年齡,并且每個文件中的年齡都是有序的(從小到大),現在需要將這5個文件整合到一個文件中,新文件的內容依然要保持有序(從小到大)。
初始化數據
1.數據生成1千萬數據(無序)。
2.將無序的數據進行排序。
3.將排好序的數據寫入到文件中。
全局變量類
package com.ymy.file;import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger;/*** 全局變量*/ public class GlobalVariable {/*** 多線程數量*/public static int threadNum = 10;/*** 線程池*/public static Executor executor = Executors.newFixedThreadPool(threadNum);/*** 數組總長度*/public static int arrLenth = 1000;/*** 隨機數范圍*/public static int DATA_FIELD = 100;/*** 每個線程的數組長度*/public static int threadLength = arrLenth/threadNum;/*** 線程同步*/public static CountDownLatch count = new CountDownLatch(10);/*** 線程安全*/public static AtomicInteger atomic = new AtomicInteger(0);public static int arr[] = new int[arrLenth];}生成隨機數方法:
private static Random r = new Random(); /*** 一次性生成1億數據* @return*/public static int[] getRandomNum1() {int num = GlobalVariable.arrLenth;int arr[] = new int[num];int i = 0;while ( i < num){arr[i] = r.nextInt(GlobalVariable.DATA_FIELD) + 1;i++;}return arr;}計數排序方法
/*** 計數排序排序** @param ages 需要排序的數組*/public static int[] sort(int[] ages) { // long startTime = System.currentTimeMillis();int length = ages.length;if (ages == null || length <= 1) {return ages;}int maxAge = ages[0];for (int i = 1; i < length; ++i) {if (maxAge < ages[i]) {maxAge = ages[i];}}System.out.println("");System.out.println("最大年齡:"+maxAge);int[] countArr = new int[maxAge + 1];for (int i = 0; i <= maxAge; i++) {countArr[i] = 0;}for (int i = 0; i < length; ++i){countArr[ages[i]]++;} // for (int i = 0; i <= maxAge; i++) { // System.out.print(countArr[i] + " "); // } // System.out.println("");// 依次累加for (int i = 1; i <= maxAge; ++i) {countArr[i] = countArr[i - 1] + countArr[i];}int[] tmpArr = new int[length];for (int i = length-1 ; i >= 0 ; --i) {int index = countArr[ages[i]]-1;tmpArr[index] = ages[i];countArr[ages[i]]--;}for (int i = 0; i < length; ++i) {ages[i] = tmpArr[i];} // long endTime = System.currentTimeMillis(); // System.out.println("排序已完成,耗時:"+(endTime-startTime)+" ms");return ages;}文件管理
package com.ymy.file;import java.io.*; import java.util.ArrayList; import java.util.List;/*** 文件管理*/ public class FileManager {private static FileReader reader1;private static FileReader reader2;private static FileReader reader3;private static FileReader reader4;private static FileReader reader5;public static StringBuffer buff= new StringBuffer();public static List<BufferedReader> readList = new ArrayList<BufferedReader>();public static List<FileWriter> writeList = new ArrayList<FileWriter>();public static File file1 = new File("C:UsersAdministratorDesktopdata1.txt");public static File file2 = new File("C:UsersAdministratorDesktopdata2.txt");public static File file3 = new File("C:UsersAdministratorDesktopdata3.txt");public static File file4 = new File("C:UsersAdministratorDesktopdata4.txt");public static File file5 = new File("C:UsersAdministratorDesktopdata5.txt");public static File file = new File("C:UsersAdministratorDesktopfinaldata.txt");private static FileWriter fos1;private static FileWriter fos2;private static FileWriter fos3;private static FileWriter fos4;private static FileWriter fos5;//需要寫入的文件public static FileWriter fos;static {try {fos1 = new FileWriter(file1, true);fos2 = new FileWriter(file2, true);fos3 = new FileWriter(file3, true);fos4 = new FileWriter(file4, true);fos5 = new FileWriter(file5, true);fos = new FileWriter(file, true);writeList.add(fos1);writeList.add(fos2);writeList.add(fos3);writeList.add(fos4);writeList.add(fos5);} catch (IOException e) {e.printStackTrace();}try {reader1 = new FileReader(file1);reader2 = new FileReader(file2);reader3 = new FileReader(file3);reader4 = new FileReader(file4);reader5 = new FileReader(file5);} catch (FileNotFoundException e) {e.printStackTrace();}}private static BufferedReader br1;private static BufferedReader br2;private static BufferedReader br3;private static BufferedReader br4;private static BufferedReader br5;static {try {br1 = new BufferedReader(new FileReader(file1));br2 = new BufferedReader(new FileReader(file2));br3 = new BufferedReader(new FileReader(file3));br4 = new BufferedReader(new FileReader(file4));br5 = new BufferedReader(new FileReader(file5));readList.add(br1);readList.add(br2);readList.add(br3);readList.add(br4);readList.add(br5);} catch (Exception e) {e.printStackTrace();}}/*** 讀數據* @param reader* @return* @throws IOException*/public static Integer readData(BufferedReader reader) throws IOException {String s = reader.readLine();return null == s ? null : Integer.valueOf(s);}/*** 輸入數據到文件** @param arr* @throws IOException*/public static void write(int[] arr, FileWriter fos) throws IOException {System.out.println("寫到文件的數據大小:"+arr.length);int length = arr.length;StringBuilder str = new StringBuilder();for (int i = 0; i < length; i++) {str.append(arr[i]);str.append("rn");}writeToFile(str.toString(),fos);}/*** 將數據寫入到文件中* @param data*/public static void writeToFile(String data,FileWriter fos){try {fos.write(data);fos.flush();} catch (IOException e) {e.printStackTrace();}}/*** 將數據寫入到最終文件夾** @param data* @throws IOException*/public static void dataDispose(int data) throws IOException {buff.append(data);buff.append("rn"); // fos.write(String.valueOf(data)); // fos.write("rn");}}準備內容已差不多,我們現在開始正式將數據分別寫入到文件中,請看main函數
/*** 初始化數據* @param args* @throws IOException*/public static void main(String[] args) throws IOException {System.out.println("初始化數據開始");long time = System.currentTimeMillis();for (int i = 0; i<FileManager.writeList.size() ;i++){System.out.println("開始第 "+i+" 文件操作");//初始化數據int[] nums = DataUtil.getRandomNum1();System.out.println("初始化數據完成,數據大小:"+nums.length);//排序int[] sort = AgeSortTest.sort(nums);System.out.println("排序完成,數據大小:"+sort.length);FileManager.write(sort,FileManager.writeList.get(i));System.out.println("寫入文件完成");}fos.close();long endTime = System.currentTimeMillis();System.out.println("耗時:"+(endTime - time)/1000 + " s");}由于我這里沒有判斷文件是否存在,所以測試的時候你需要先將txt文件建好,否者會報錯,運行main函數,我們來看文件數據
看樣子文件應該是已經生成成功了,那文件中到底有沒有數據呢?請看:
數據剛好是1千萬,這是data1文件的數據,其他4個文件中的數據也是類似的,這里就不展示了。
將文件合并寫入新文件并保證數據仍然有序
思路:1.分別從5個文件中獲取第一條數據。2.比較5條數據的大小,找出最小的一條。3.將最小的數據寫入到新文件中。4.在最小一條所在的文件中繼續取出一條。5.繼續比較5條數據的大小,后面重復上面步驟,直到數據被全部讀取完成。
請看main函數
/*** 將文件按順序寫入到新文件中* @param args* @throws IOException*/public static void main(String[] args) throws IOException {long time = System.currentTimeMillis();List<BufferedReader> readList = FileManager.readList;int readListSize = readList.size();int[] arr = new int[readListSize];//讀取每個文件第一行,將數據賦值給數組for (int i = 0; i < readListSize; i++) {arr[i] = FileManager.readData(readList.get(i));}int index = DataUtil.comp(arr);FileManager.dataDispose(arr[index]);while(readList.size() > 1){Integer data = FileManager.readData(readList.get(index));if(null == data){readList.remove(index);//從新給數組賦值arr = set(arr,index);}else{arr[index] = data;}index = DataUtil.comp(arr);FileManager.dataDispose(arr[index]);}//最后將for(;;){Integer lastData = FileManager.readData(readList.get(0));if(null == lastData){break;}FileManager.dataDispose(lastData);}FileManager.writeToFile(FileManager.buff.toString(), fos);fos.flush();fos.close();long endTime = System.currentTimeMillis();System.out.println("操作完成!");System.out.println("操作用時:"+(endTime-time));}數據對比函數
/*** 數據對比* @param arr* @return*/public static int comp(int[] arr){//判斷數據大小int index = 0;for (int i = 1; i < arr.length; i++) {if(arr[index] > arr[i] ){index = i;}}return index;}數組重排
/*** 數組重排* @param arr* @param index* @return*/public static int[] set(int arr[], int index){if(arr.length == 1){return arr;}int[] newArr = new int[arr.length-1];for(int i = 0; i< arr.length-1;i++ ){if(i != index ){newArr[i] = arr[i+1];}else{newArr[i] = arr[i];}}return newArr;}運行main函數
發現耗時7秒,這速度是塊還是慢呢?一個文件一千萬數據,5個文件就是5千萬,5千萬的讀取以及5千萬的寫入耗時7秒,這里的讀取是一行一行的讀取,但是寫入的時候是一次性往新文件中寫入5千萬數據,那還有沒有優化的空間呢?讓時間更短一些,其實是有的。1.我們之前做的是一行行的讀取,我們可以改為一次讀取多行放在內存中,cpu直接往內存中獲取數據,獲取一條,刪除一條,當內存中的數據被刪完時,也就代表內存中的數據已經全被使用,這時候就再次往磁盤中讀取數據,一次類推,這樣也能提升不少性能。2.如果你仔細查看代碼你會發現,這里的所有操作都是串行化,第一步:獲取到數據才能進行對比;第二步:對比數據;第三步:將最小的數據寫入到新文件中;不知道你發現沒有,第一步獲取數據需要第二部的支持(找到最小的那條數據)才能繼續獲取數據,但是和第三步并沒有任何關系,所以,如果我們能將第一、二步串行;第三步與第一、二步并行,是否能減少運行時間呢?可以考慮Disruptor隊列實現。
總結
為什么要使用計數排序
初始化數據的時候我使用了計數排序,我為什么會選擇他呢?為什么不選擇快排這樣的排序方式?
我先介紹一下什么是計數排序:計數排序是一個非基于比較的排序算法,該算法于1954年由 Harold H. Seward 提出。它的優勢在于在對一定范圍內的整數排序時,它的復雜度為Ο(n+k)(其中k是整數的范圍),快于任何比較排序算法。 當然這是一種犧牲空間換取時間的做法,而且當O(k)>O(n*log(n))的時候其效率反而不如基于比較的排序(基于比較的排序的時間復雜度在理論上的下限是O(n*log(n)), 如歸并排序,堆排序)。
由于我們的年齡區間范圍并不大,很合適計數排序的要求,所以這里選擇計數排序會比快速排序、歸并排序等等快很多,如果區間范圍太大,計數排序就不適用了,所以使用的時候還是要慎重考慮。
數據初始化是使用多線程是否會比較快?
答案是否定的,你可能會疑惑,多線程不就是來提高性能的嗎?使用多線程來初始化數據還是變慢呢?
我們的數據都是一次性生成在內存中,然后再一次性寫入到磁盤中,內存中的運行速度是很快的,多線程會牽扯到上下文的切換,這就會讓cpu寄存器花更多的時間去保存當前被中斷的堆棧,而單線程則不同,因為在內存中,沒有線程切換所帶來的額外消耗,一個線程會跑的更快,如果我們操作的是磁盤,那么我們就可以考慮使用多線程,因為磁盤的效率很低,會讓cpu長期處理空閑時間,所以這時候使用多線程會提高程序的效果,用過redis的都知道,redis就是單線程應用,但是他同樣可以達到讀:10w/s ;寫:8w/s,就是因為redis都是再內存中操作沒有上下文的切換,性能會被發揮的很好,這時候你發現內存還是跟不上cpu的速度啊,應該還是有空閑時間,隨著技術的發展,我們引入了cpu緩存,也就是cpu需要操作數據的時候會將內存的數據加載到緩存中,后面直接操作緩存即可,cpu往內存拿數據的時候并不是只拿指定的數據,他會獲取指定的數據以及它周圍的一部分數據,cpu認為它被訪問了,那么他周圍的數據也有很大可能被訪問,這就是有序數組的隨機訪問比鏈表的訪問速度塊的原因之一。
既然是多線程,那么必然會伴隨著線程安全問題,我們還要對共享的數據加鎖或者進行CAS處理,速度也會受限制,什么時候使用多線程,應該具體問題具體分析,并不是多線程就一定會比單線程速度快。
總結
以上是生活随笔為你收集整理的将文件中所有数读到一个数组中_「数据结构与算法」将5个文件中的一千万年龄合并到一个新文件中...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 研究生举报导师强迫学生延期毕业,事件再三
- 下一篇: 2021年图灵奖公布!72岁的美国科学家