对八位的无序电话号码文件进行排序,内存有限,不能将文件直接读取到内存中,所以不能使用冒泡、快速等算法进行排序,而硬盘有足够的空间,如何做?
package demo.sort; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.LineNumberReader; import java.util.ArrayList; import java.util.List; import org.junit.Test; import demo.time.Timer; public class BitMapSort { private byte[] baseByte =new byte[]{ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, (byte) (0x80 ) }; public static final int BYTE_LENGTH = 8; /** 取值范围:最大值减最小值 +1 */ private int max_sub = 0; /** 取值开始值 */ private int num_start = 0; @Test public void test() { sortByBitMap("F:/ProgrammingPearls/randnum1.txt", "F:/ProgrammingPearls/sort3.txt", 10000000, 90000000); } /** * * @param sourcesName 待排序文件 * @param targetName 目标文件 * @param start_num 开始的数字 * @param range 数字取值范围,最大-最小+1 */ public void sortByBitMap(String sourcesName, String targetName, int start_num, int range){ setNum_start(start_num); setMax_sub(range); Timer timer = new Timer(); byte[] sortMap = initBitMap(sourcesName, range/ BYTE_LENGTH); timer.printRunTime(); printToFile(targetName,sortMap); timer.printRunTime(); } private void printToFile(String outputFileName , byte[] content ){ //System.out.println("111\n".replaceAll("\\D", "") +"--------"); File file = new File(outputFileName); FileWriter fileWritter; BufferedWriter bufferWritter; try { fileWritter = new FileWriter(file,true); bufferWritter = new BufferedWriter(fileWritter); for(int i =0; i< content.length;i++){ //System.out.println("sortMap[i]="+sortMap[i]); int[] nums = getSortFromByte(content[i] ); for(int temp: nums){ int sortElement = num_start + temp + i * BYTE_LENGTH; try { bufferWritter.write(sortElement + "\n\r"); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } //System.out.println(sortElement); } } bufferWritter.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private byte[] initBitMap(String path, int count){ //String path = "F:/ProgrammingPearls/randnum1.txt"; File file = new File(path); FileReader in = null; LineNumberReader reader = null; try { in = new FileReader(file); reader = new LineNumberReader(in); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } byte[] sortMap = new byte[count]; String numStr = ""; while(numStr != null){ try { numStr = reader.readLine(); recordToByte(sortMap, numStr); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return sortMap; } private void recordToByte(byte[] bytes, String numStr){ if(numStr == null || numStr.equals("")){ return; } numStr = numStr.replaceAll( "\\D", ""); //System.out.println("----->>numStr="+numStr); try{ final int num = Integer.parseInt(numStr); int index = (num- num_start )/ BYTE_LENGTH ; int bitIndex = num % BYTE_LENGTH; bytes[index] = recordToByte(bytes[index], bitIndex); }catch(Exception e){ e.printStackTrace(); } } private byte recordToByte(final byte bit, final int bitindex){ byte result = bit; if(bitindex >= 0 && bitindex < 8){ result = (byte) (bit | baseByte[bitindex]); } return result; } private int[] getSortFromByte(final byte bit ){ // int[] result = new int[8]{-1}; List<Integer> result = new ArrayList<Integer>(); for(int i=0;i< 8;i++){ if( (bit & baseByte[i]) != 0){ result.add(i); } } int[] result2 = new int[result.size()]; for(int i =0; i <result2.length ;i++ ){ result2[i] = result.get(i); } return result2; } public int getMax_sub() { return max_sub; } public void setMax_sub(int max_sub) { this.max_sub = max_sub; } public int getNum_start() { return num_start; } public void setNum_start(int num_start) { this.num_start = num_start; } }
初始化位图运行时间为:875.0毫秒
将位图写入排序文件运行时间为:1043.0毫秒
合计耗时:1913毫秒
计算机处理器:i5
文件中共有一百万个八位整数,从1000 0000 到9999 9999,共计有9000 0000个数字,申请一个1125 0000个byte数组 ≈ 10.73MB
如果使用内存排序(如冒泡、快速等)需要 100 0000 * 4B ≈ 3.814MB,请注意,这里的数据量只有一百万个,但是数字有九千万种可能,位图排序如果是全集的话,仍然只需要10M多的内存,但是内存排序却需要 九十倍,也就是270M的存储内存,这里并不考虑时间上的效率。也就是每个数字,需要消耗4B内存(int型),当在此例中,文件中的无序数字为 2812500个时,内存排序所需空间,要超过位图排序。请注意,此时的两百万只占九千万的 2%多一些,当样本数进一步增大时, 其消耗的内存会进一步成倍增长。
如果排序的样本不是八位数,而是9位 10位呢?很显然,内存排序根本吃不消,但是位图排序,却可以在多次扫描文件的方式实现,其内存消耗几乎不增长。
此处,可见位图排序的优势所在
1. 内存消耗有限,与样本数无关,与取样范围有关(内存少)
2. 耗时只花在读文件和写文件时,排序几乎没有耗费时间(耗时少)
3. 如果是对9位数 10位数进行排序,可以多次读取文件,即使是一次读取,那么耗费内存的增长量来说,也是极其合算的,当内存有限制时,几乎无法对超大样本进行快速排序,此也即为位图排序的应用场景。(内存有限,而硬盘无限)
五、思考
如果文件中的样本不是每个取值唯一,而是有重复该如何使用位图排序?