多路归并排序 外排序 大文件排序 java实现

 
  1. package merge;   
  2.   
  3. import java.io.BufferedReader;   
  4. import java.io.BufferedWriter;   
  5. import java.io.File;   
  6. import java.io.FileReader;   
  7. import java.io.FileWriter;   
  8. import java.text.SimpleDateFormat;   
  9. import java.util.ArrayList;   
  10. import java.util.Iterator;   
  11. import java.util.LinkedHashSet;   
  12. import java.util.List;   
  13. import java.util.Set;   
  14. import java.util.SortedSet;   
  15. import java.util.TreeSet;   
  16.   
  17. /**  
  18.  *   
  19.  * 外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,  
  20.  * 需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。  
  21.  * 外部排序最常用的算法是多路归并排序,即将原文件分解成多个能够一次性装人内存的部分,  
  22.  * 分别把每一部分调入内存完成排序。然后,对已经排序的子文件进行归并排序。  
  23.  *   
  24.  * @author jia.hej  
  25.  *  
  26.  * @version $Id: MergeSort.java, v 0.1 2009-8-7 下午03:53:51 jia.hej Exp $  
  27.  */  
  28. public class MergeSort {   
  29.   
  30.     /** 十 */  
  31.     private static long   TEN              = 10;   
  32.     /** 百 */  
  33.     private static long   HUNDRED          = 100;   
  34.     /** 千 */  
  35.     private static long   THOUSAND         = 1000;   
  36.     /** 万 */  
  37.     private static long   MILLION          = 10000;                 //1078  00:00:01 078   
  38.     /** 十万 */  
  39.     private static long   TEN_MILLION      = 100000;                //9656  00:00:09 656   
  40.     /** 百万 */  
  41.     private static long   HUNDRED_MILLION  = 1000000;               //93733  00:01:33 733   
  42.     /** 千万 */  
  43.     private static long   THOUSAND_MILLION = 10000000;              //970144  00:16:10 144   
  44.     /** 亿 */  
  45.     private static long   BILLION          = 100000000;   
  46.     /** 十亿 */  
  47.     private static long   TEN_BILLION      = 1000000000;   
  48.     /** 百亿 */  
  49.     private static long   HUNDRED_BILLION  = 10000000000l;   
  50.     /** 千亿 */  
  51.     private static long   THOUSAND_BILLION = 100000000000l;   
  52.   
  53.     private static String INPUT_FILE       = "c:\\test\\input.txt";   
  54.   
  55.     private static String OUTPUT_FILE      = "c:\\test\\output.txt";   
  56.   
  57.     /** 拆分大小 */  
  58.     private static int    SPLIT_SIZE       = 10 * 10000;   
  59.   
  60.     private static int    numSize;   
  61.   
  62.     public static void main(String[] args) throws Exception {   
  63.         createDir("c:\\test");   
  64.         createFile(INPUT_FILE);   
  65.         numSize = createRandomNum(THOUSAND_MILLION);   
  66.   
  67.         sortFile(INPUT_FILE);   
  68.   
  69.         long beginTime = System.currentTimeMillis();   
  70.         System.out.println("begin=" + beginTime);   
  71.   
  72.         //拆分文件   
  73.         splitFile(INPUT_FILE, numSize);   
  74.   
  75.         List<String> splitFilePathList = new ArrayList<String>();   
  76.         File dir = new File("c:\\test\\temp");   
  77.         File[] files = dir.listFiles();   
  78.         for (int i = 0; i < files.length; i++) {   
  79.             File file = files[i];   
  80.             splitFilePathList.add(file.getAbsolutePath());   
  81.         }   
  82.         //合并文件   
  83.         createFile(OUTPUT_FILE);   
  84.         mergeFile(splitFilePathList, OUTPUT_FILE);   
  85.   
  86.         long endTime = System.currentTimeMillis();   
  87.         System.out.println("end=" + endTime);   
  88.         System.out.println("end-begin=" + (endTime - beginTime));   
  89.         SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");   
  90.         System.out.println(simpleDateFormat.format(endTime - beginTime));   
  91.   
  92.         //删除拆分文件   
  93.         System.gc();   
  94.         Runtime.getRuntime().exec(new String[] { "cmd""/c""del""c:\\test\\temp\\*.txt" });   
  95.     }   
  96.   
  97.     private static void sortFile(String path) throws Exception {   
  98.         SortedSet<Integer> set = new TreeSet<Integer>();   
  99.         File file = new File(path);   
  100.         FileReader fileReader = new FileReader(file);   
  101.         BufferedReader bufferedReader = new BufferedReader(fileReader);   
  102.         String value;   
  103.         while ((value = bufferedReader.readLine()) != null) {   
  104.             set.add(Integer.parseInt(value));   
  105.         }   
  106.         bufferedReader.close();   
  107.         fileReader.close();   
  108.         createFile("c:\\test\\input排序.txt");   
  109.         writeFile("c:\\test\\input排序.txt", set, false);   
  110.     }   
  111.   
  112.     /**  
  113.      * 拆分文件  
  114.      *   
  115.      * @param inputPath  
  116.      * @param numSize  
  117.      * @throws Exception  
  118.      */  
  119.     private static void splitFile(String inputPath, int numSize) throws Exception {   
  120.         File file = new File(inputPath);   
  121.         FileReader fileReader = new FileReader(file);   
  122.         BufferedReader bufferedReader = new BufferedReader(fileReader);   
  123.         SortedSet<Integer> set = new TreeSet<Integer>();   
  124.         String str;   
  125.         createDir("c:\\test\\temp");   
  126.         if (numSize > SPLIT_SIZE) {   
  127.             int count = 1;   
  128.             int fileNum = 1;   
  129.             while ((str = bufferedReader.readLine()) != null) {   
  130.                 set.add(Integer.parseInt(str));   
  131.                 //超过拆分数,写入子文件   
  132.                 if (count >= SPLIT_SIZE) {   
  133.                     createFile("c:\\test\\temp\\" + fileNum + ".txt");   
  134.                     writeFile("c:\\test\\temp\\" + fileNum + ".txt", set, false);   
  135.                     set.clear();   
  136.                     count = 0;   
  137.                     fileNum++;   
  138.                 }   
  139.                 count++;//读取文件当前行数   
  140.             }   
  141.         }   
  142.         //总量未达到拆分数,写入子文件   
  143.         else {   
  144.             while ((str = bufferedReader.readLine()) != null) {   
  145.                 set.add(Integer.parseInt(str));   
  146.             }   
  147.             createFile("c:\\test\\temp\\1.txt");   
  148.             writeFile("c:\\test\\temp\\1.txt", set, false);   
  149.         }   
  150.         if (bufferedReader != null) {   
  151.             bufferedReader.close();   
  152.         }   
  153.         if (fileReader != null) {   
  154.             fileReader.close();   
  155.         }   
  156.     }   
  157.   
  158.     /**  
  159.      * 合并文件  
  160.      *   
  161.      * <p>  
  162.      *    1.txt(1、3、5、7、9)和2.txt(6、8、9)<br/>  
  163.      *    首先1和6进入treeset。 <br/>  
  164.      *    输出1,发现是来自于1.txt的,再读入3,此时set中的元素是6和3。<br/>   
  165.      *    输出3,发现还是来自于1.txt的,再读入5,此时set中的元素是6和5。 <br/>  
  166.      *    输出5,发现还是来自于1.txt的,再读入7,此时set中的元素是6和7。 <br/>  
  167.      *    输出6,发现来自于2.txt的,读入8,此时set中的元素是8和7。 <br/>  
  168.      *    输出7,发现来自于1.txt的,读入9,此时set中的元素是8和9。 <br/>  
  169.      *    输出8,发现来自于2.txt的,读入9,此时set中的元素是9和9。  
  170.      * </p>  
  171.      *   
  172.      * @param splitFilePathList  
  173.      * @param outputPath  
  174.      * @throws Exception  
  175.      */  
  176.     private static void mergeFile(List<String> splitFilePathList, String outputPath)   
  177.                                                                                     throws Exception {   
  178.         //fileInfo添加到set   
  179.         SortedSet<FileInfo> fileInfoSet = new TreeSet<FileInfo>(new FileInfoComparator());   
  180.         if (fileInfoSet.isEmpty()) {   
  181.             for (int i = 0; i < splitFilePathList.size(); i++) {   
  182.                 File file = new File(splitFilePathList.get(i));   
  183.                 FileReader fileReader = new FileReader(file);   
  184.                 BufferedReader bufferedReader = new BufferedReader(fileReader);   
  185.   
  186.                 FileInfo fileInfo = new FileInfo();   
  187.                 String splitFilePath = splitFilePathList.get(i);   
  188.                 fileInfo.setFileNum(Integer.parseInt(splitFilePath.substring(splitFilePath   
  189.                     .lastIndexOf("\\") + 1, splitFilePath.indexOf(".txt"))));//文件号   
  190.                 fileInfo.setReader(bufferedReader);//reader引用   
  191.                 String value = bufferedReader.readLine();   
  192.                 if (value != null) {   
  193.                     fileInfo.setValue(value);//当前值   
  194.                     fileInfo.setLineNum(fileInfo.getLineNum() + 1);//当前行号   
  195.                     fileInfoSet.add(fileInfo);   
  196.                 }   
  197.             }   
  198.         }   
  199.   
  200.         Set<Integer> valueSet = new LinkedHashSet<Integer>();   
  201.         boolean isSplit = false;   
  202.         int count = 1;   
  203.         //输出set元素   
  204.         while (!fileInfoSet.isEmpty()) {   
  205.             FileInfo currentFileInfo = fileInfoSet.first();   
  206.             valueSet.add(Integer.parseInt(currentFileInfo.getValue()));   
  207.             //拆分批量写入文件   
  208.             if (valueSet.size() >= SPLIT_SIZE) {   
  209.                 writeFile(outputPath, valueSet, true);   
  210.                 valueSet.clear();   
  211.                 isSplit = true;   
  212.             }   
  213.   
  214.             //clone fileInfo   
  215.             FileInfo nextFileInfo = new FileInfo();   
  216.             nextFileInfo.setFileNum(currentFileInfo.getFileNum());   
  217.             nextFileInfo.setLineNum(currentFileInfo.getLineNum());   
  218.             nextFileInfo.setValue(currentFileInfo.getValue());   
  219.             nextFileInfo.setReader(currentFileInfo.getReader());   
  220.   
  221.             boolean isSuccess = nextFileInfo.readNextValue();   
  222.   
  223.             //未到文件末尾,set中fileInfo重新排序   
  224.             if (isSuccess) {   
  225.                 if (fileInfoSet.remove(currentFileInfo)) {   
  226.                     fileInfoSet.add(nextFileInfo);   
  227.                 }   
  228.             }   
  229.             //已到文件末尾,从set中移除该fileInfo   
  230.             else {   
  231.                 fileInfoSet.remove(currentFileInfo);   
  232.             }   
  233.   
  234.             System.out.println("----- MergeFile:" + count++ + " -----");   
  235.             System.out.println("fileNum=" + currentFileInfo.getFileNum());   
  236.             System.out.println("lineNum=" + currentFileInfo.getLineNum());   
  237.             System.out.println("value=" + currentFileInfo.getValue());   
  238.             System.out.println("----------------------------");   
  239.         }   
  240.   
  241.         //从未拆分过则一次性写入文件   
  242.         if (valueSet.size() > 0 && valueSet.size() < SPLIT_SIZE && !isSplit) {   
  243.             writeFile(outputPath, valueSet, false);   
  244.         }   
  245.         //曾拆分过剩余部分写入文件   
  246.         else if (valueSet.size() > 0 && valueSet.size() < SPLIT_SIZE && isSplit) {   
  247.             writeFile(outputPath, valueSet, true);   
  248.         }   
  249.     }   
  250.   
  251.     /**  
  252.      * 生成随机数  
  253.      *   
  254.      * @param numSize  
  255.      * @return  
  256.      * @throws Exception  
  257.      */  
  258.     private static int createRandomNum(long numSize) throws Exception {   
  259.         Set<Integer> set = new LinkedHashSet<Integer>();   
  260.         int count = 0;   
  261.         boolean isSplit = false;   
  262.         while (count < numSize) {   
  263.             int num = (int) (Math.random() * numSize + 1);   
  264.             if (set.add(num)) {   
  265.                 count++;   
  266.             }   
  267.             //拆分批量写入文件   
  268.             if (set.size() >= SPLIT_SIZE) {   
  269.                 writeFile(INPUT_FILE, set, true);   
  270.                 set.clear();   
  271.                 isSplit = true;   
  272.             }   
  273.         }   
  274.   
  275.         //从未拆分过则一次写入文件   
  276.         if (set.size() > 0 && set.size() < SPLIT_SIZE && !isSplit) {   
  277.             writeFile(INPUT_FILE, set, false);   
  278.         }   
  279.         //曾拆分过剩余部分写入文件   
  280.         else if (set.size() > 0 && set.size() < SPLIT_SIZE && isSplit) {   
  281.             writeFile(INPUT_FILE, set, true);   
  282.         }   
  283.         return count;   
  284.     }   
  285.   
  286.     private static void createDir(String dirPath) {   
  287.         File dir = new File(dirPath);   
  288.         if (!dir.exists()) {   
  289.             if (dir.mkdir()) {   
  290.                 System.out.println(dir.getName() + " is create.");   
  291.             }   
  292.         }   
  293.     }   
  294.   
  295.     private static void createFile(String path) throws Exception {   
  296.         File file = new File(path);   
  297.         if (!file.exists()) {   
  298.             if (file.createNewFile()) {   
  299.                 System.out.println(file.getName() + " is create.");   
  300.             }   
  301.         }   
  302.     }   
  303.   
  304.     private static void writeFile(String path, Set<Integer> set, boolean isAppend) throws Exception {   
  305.         File file = new File(path);   
  306.         FileWriter fileWriter = new FileWriter(file, isAppend);// 第二个参数表示:是否为追加模   
  307.         BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);   
  308.         Iterator<Integer> iterator = set.iterator();   
  309.         while (iterator.hasNext()) {   
  310.             bufferedWriter.write(iterator.next().toString());   
  311.             bufferedWriter.newLine();   
  312.         }   
  313.         bufferedWriter.flush();   
  314.         if (bufferedWriter != null) {   
  315.             bufferedWriter.close();   
  316.         }   
  317.         if (fileWriter != null) {   
  318.             fileWriter.close();   
  319.         }   
  320.     }   
  321. }  
  1. package merge;   
  2.   
  3. import java.io.BufferedReader;   
  4.   
  5. /**  
  6.  *   
  7.  * 文件信息  
  8.  *   
  9.  * @author jia.hej  
  10.  *  
  11.  * @version $Id: FileInfo.java, v 0.1 2009-8-1 上午02:11:30 jia.hej Exp $  
  12.  */  
  13. public class FileInfo {   
  14.   
  15.     /**  
  16.      * 文件号  
  17.      */  
  18.     private int            fileNum;   
  19.   
  20.     /**  
  21.      * 当前行号  
  22.      */  
  23.     private int            lineNum = 0;   
  24.   
  25.     /**  
  26.      * 当前值  
  27.      */  
  28.     private String         value;   
  29.   
  30.     /**  
  31.      * BufferedReader引用  
  32.      */  
  33.     private BufferedReader reader;   
  34.   
  35.     public boolean readNextValue() throws Exception {   
  36.         String value;   
  37.         if ((value = this.reader.readLine()) != null) {   
  38.             this.value = value;   
  39.             this.lineNum++;   
  40.             return true;   
  41.         } else {   
  42.             this.reader.close();   
  43.             return false;   
  44.         }   
  45.     }   
  46.   
  47.     public int getFileNum() {   
  48.         return fileNum;   
  49.     }   
  50.   
  51.     public void setFileNum(int fileNum) {   
  52.         this.fileNum = fileNum;   
  53.     }   
  54.   
  55.     public int getLineNum() {   
  56.         return lineNum;   
  57.     }   
  58.   
  59.     public void setLineNum(int lineNum) {   
  60.         this.lineNum = lineNum;   
  61.     }   
  62.   
  63.     public String getValue() {   
  64.         return value;   
  65.     }   
  66.   
  67.     public void setValue(String value) {   
  68.         this.value = value;   
  69.     }   
  70.   
  71.     public BufferedReader getReader() {   
  72.         return reader;   
  73.     }   
  74.   
  75.     public void setReader(BufferedReader reader) {   
  76.         this.reader = reader;   
  77.     }   
  78. }  

 

  1. package merge;   
  2.   
  3. import java.util.Comparator;   
  4.   
  5. /**  
  6.  *   
  7.  * 文件比较器  
  8.  *   
  9.  * @author jia.hej  
  10.  *  
  11.  * @version $Id: FileInfoComparator.java, v 0.1 2009-8-7 下午01:42:05 jia.hej Exp $  
  12.  */  
  13. public class FileInfoComparator implements Comparator<FileInfo> {   
  14.   
  15.     public int compare(FileInfo o1, FileInfo o2) {   
  16.         if (Integer.parseInt(o1.getValue()) != Integer.parseInt(o2.getValue())) {   
  17.             return Integer.parseInt(o1.getValue()) - Integer.parseInt(o2.getValue());   
  18.         }   
  19.         //如果存在重复值则使用文件号比较   
  20.         else {   
  21.             return o1.getFileNum() - o2.getFileNum();   
  22.         }   
  23.     }   
  24.   
  25. }  

 

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值