大数据排序,理论上支持个100亿条没问题吧,o(∩_∩)o 哈哈 --给爱吃大肉包补充了注释

爱吃大肉包在文章《大数据排序,理论上支持个100亿条没问题吧,o(∩_∩)o 哈哈》中给出了一个大数据排序的源码。经过运行,成功了。

万恶的是,这人没写注释,读起来很不方便,因此,在自己机器上添加了注释后 ,上传上来。

做了一些小改动,

①为了可以重复测试,增加了删除目录下的所有文件的方法

②增加了计算耗时的方法

③增加了System.out.println();的方法,打印最后的结果集(数据量小时可以直观的查看结果。)主要是便于理解这个思路。

文章的原路径是:http://www.oschina.net/code/snippet_867417_19341?p=2#comments 点击打开链接


问题来自《Programming Perl》

楔子: 问题:假设一个文件中有9亿条不重复的9位整数,现在要求对这个文件进行排序。
一般解题思路: 1、将数据导入到内存中 2、将数据进行排序 (比如插入排序、快速排序) 3、将排序好的数据存入文件
难题: 一个整数为4个字节即使使用数组也需要900,000,000 * 4byte = 3.4G内存对于32位系统,访问2G以上的内存非常困难,而且一般设备也没有这么多的物理内存将数据完全导入到内存中的做法不现实。
其他解决办法: 1、导入数据库运算 2、分段排序运算 3、使用bit位运算
解决方案一:数据库排序 将文本文件导入到数据库,让数据库进行索引排序操作后提取数据到文件
优点:操作简单缺点:运算速度慢,而且需要数据库设备。
解决方案二:分段排序 操作方式:规定一个内存大小,比如200M,200M可以记录52428800条记录,我们可以每次提取5000万条记录到文件进行排序,要装满9位整数需要20次,所以一共要进行20次排序,需要对文件进行20次读操作
缺点: 编码复杂,速度也慢(至少20次搜索)
关键步骤:先将整个9位整数进行分段,亿条数据进行分成20段,每段5000万条,在文件中依次搜索0~5000万,50000001~1亿…… 将排序的结果存入文件
解决方案三:bit位操作 思考下面的问题: 一个最大的9位整数为999999999 这9亿条数据是不重复的,可不可以把这些数据组成一个队列或数组,让它有0~999999999(10亿个)元素数组下标表示数值,节点中用0表示这个数没有,1表示有这个数,判断0或1只用一个bit存储就够了


下面是我添加注释后的代码 ,第二种思路

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * 
 * @author Hejinbin QQ 277803242 email qing878@gmail.com
 */
public class BigDataSort {

	public final static String SMALL_FILE_PATH = "e://temp//BigData//";
	public final static int BIG_NUM_LINE = 1000000;// 务必保证BIG_NUM_LINE 是
													// SMALL_FILE_LINE的倍数
	public final static String ORING_FILE_PATH = "e://temp//BigData//bigData.txt";
	public final static int SMALL_FILE_LINE = 100000; // 1M for 1 small file
	private File tempFiles[];

	public BigDataSort() throws IOException {
		// 补充方法 删除所有文件
		deleteAllFile();
		// 删除所有的。temp文件
		deleteAlltempFile();
		// 将BIG_NUM_LINE条int值,放到指定的文件ORING_FILE_PATH中,每行一个
		createBigsortNums();
		// 拆分成小文件排序,(需要明确计算出小文件的大小不能超出可用内存)【编程珠玑 programming perl】中的问题
		beSmallFileAndSort();
		unitFileToSort();
	}

	private void createBigsortNums() throws IOException {
		BufferedWriter writer = new BufferedWriter(new FileWriter(
				ORING_FILE_PATH));
		Random random = new Random();
		for (int i = 0; i < BIG_NUM_LINE; i++) {
			writer.write(String.valueOf(random.nextInt(100000000)));
			writer.newLine();// add a new line . in order to show easy by file
		}
		writer.close();
	}

	private void beSmallFileAndSort() throws IOException {
		BufferedReader bigDataFile = new BufferedReader(new FileReader(
				ORING_FILE_PATH));
		List<Integer> smallLine = null;
		String str = "";
		// 根据大文件和小文件的数量,生成n个小文件
		int fileCount = BIG_NUM_LINE / SMALL_FILE_LINE;
		tempFiles = new File[fileCount];
		for (int i = 0; i < tempFiles.length; i++) {
			tempFiles[i] = new File(SMALL_FILE_PATH + "sortTempFile" + i
					+ ".temp");
			BufferedWriter smallWtite = new BufferedWriter(new FileWriter(
					tempFiles[i]));
			smallLine = new ArrayList<Integer>();
			// 向n个文件中插入字符串,每个文件长度为small_file_line,最后一个装入剩余数量
			for (int j = 0; j < SMALL_FILE_LINE; j++) {
				smallLine.add(Integer.parseInt(bigDataFile.readLine()));
			}
			// 使用Collections的排序
			Collections.sort(smallLine);
			// 逐行写入文件
			for (Object num : smallLine.toArray()) {
				smallWtite.write(num + "\n");
			}
			// 每写完一个文件,需要将IO流关闭
			smallWtite.close();
		}
	}

	private void unitFileToSort() throws IOException {
		File tempFile = null;
		// 以第一个文件为基准,其余的文件向第一个文件写入临时文件
		for (int i = 1; i < tempFiles.length; i++) {
			tempFile = sortBySmallFile(tempFiles[0], tempFiles[i]);
			// 删除第一个文件
			tempFiles[0].delete();
			// 将合并后的临时文件放入第一个文件中,此时,第一个文件已经是0,...i个文件的合并后的结果
			tempFiles[0] = tempFile;
		}
		tempFile.renameTo(new File(ORING_FILE_PATH + "sortResult.txt"));

		BufferedReader br = new BufferedReader(new FileReader(new File(
				ORING_FILE_PATH + "sortResult.txt")));

		// String str = "";
		// while (null != (str = br.readLine())) {
		// System.out.println(str);
		// }打印比较耗时
		br.close();
	}

	/**
	 * 
	 * @param fromFile
	 *            第一个文件 A (为了便于注释,将文件命名为 A 、B ,结果文件命名为C)
	 * @param toFile
	 *            第二个文件 B
	 * @return
	 * @throws IOException
	 */
	public static File sortBySmallFile(File fromFile, File toFile)
			throws IOException {
		BufferedReader fromRd = new BufferedReader(new FileReader(fromFile));
		BufferedReader toTempRd = new BufferedReader(new FileReader(toFile));
		// 临时的排序的结果文件 C
		File newSortFile = new File(SMALL_FILE_PATH + fromFile.getName()
				+ ".temp");
		// File newSortFile = new File(SMALL_FILE_PATH + "sortTempFile.temp");

		BufferedWriter newSortFileWt = new BufferedWriter(new FileWriter(
				newSortFile));
		int index = -1;
		int toPoint = -1;
		// 【1-0】判断此流是否已准备好被读取。如果缓冲区不为空,或者底层字符流已准备就绪,则缓冲的字符流准备就绪。
		while (fromRd.ready()) {
			index = Integer.parseInt(fromRd.readLine());
			if (index < toPoint) {
				// 【1-1】 第一个文件A中的数值 小于 第二个文件B的数值,将第一个文件数值写入C,
				newSortFileWt.write(String.valueOf(index));
				newSortFileWt.newLine();
				// 跳出此次循环,进入下一个循环
				continue;
			} else {
				// 【1-2】 第一个文件A中的数值 大于 第二个文件B的数值,(-1时,首次读取B中的数值)将第二个文件数值写入C,
				if (toPoint != -1) {
					newSortFileWt.write(String.valueOf(toPoint));
					newSortFileWt.newLine();
				}
				// 继续读取第二个文件中的数值
			}
			while (toTempRd.ready()) {
				// 【2-0】 读取第二个文件中的数值
				toPoint = Integer.parseInt(toTempRd.readLine());
				if (toPoint < index) {
					// 【2-1】 第二个文件B中的数值 小于
					// 第一个文件A的数值,(先读的A,因此,index不肯为-1)将第二个文件数值写入C,
					newSortFileWt.write(String.valueOf(toPoint));
					newSortFileWt.newLine();
					// 继续读取第二个文件
				} else {
					// 【2-2】 第二个文件B中的数值 大于 第一个文件A的数值, 将第一个文件数值写入C,
					newSortFileWt.write(String.valueOf(index));
					newSortFileWt.newLine();
					// 停止读取第二个文件B的while循环
					break;
				}
			}// 隐藏的逻辑:当A先读完,则B中的数值为较大数值,第二层的while语句会将B中的数值读出并写入C
				// 若B先读完,A中的值较大,则执行注释中的【1-2】部分的代码,因为B文件已读完,toTempRd.ready()为false,不在执行【2-0】部分去读取B文件

		}
		newSortFileWt.write(String.valueOf(index > toPoint ? index : toPoint));
		newSortFileWt.newLine();
		newSortFileWt.close();
		fromRd.close();
		toTempRd.close();
		// toFile.delete();
		return newSortFile;
	}

	private void deleteAllFile() {
		File f = new File(SMALL_FILE_PATH);
		File[] files;
		if (f.isDirectory()) {
			files = f.listFiles();
			for (File var : files) {
				var.delete();
			}
		}
	}

	private void deleteAlltempFile() {
		File f = new File(SMALL_FILE_PATH);
		File[] files;
		if (f.isDirectory()) {
			// 获取.temp结尾的文件
			files = f.listFiles(new FilenameFilter() {
				public boolean accept(File dir, String name) {
					if (name.endsWith(".temp")) {
						return true;
					}
					return false;
				}
			});

			for (File var : files) {
				var.delete();
			}
		}

	}

	public static void main(String[] args) throws IOException {
		Long before = System.currentTimeMillis();
		BigDataSort bigDataSort = new BigDataSort();

		Long after = System.currentTimeMillis();
		System.out.println("共耗用 " + (after - before) + " 毫秒");

		// 1000000 100000共耗用 2575 毫秒
		// 10000000 1000000共耗用 共耗用 21510 毫秒
		// 推论, 100亿,20000秒
	}

	// 一般分成10个文件后,需要19个临时小文件然后删除,确实挺高潮....
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值