我准备用优先队列来实现TopN问题,海量数据是指计算机内存装不下的很大的数据文件,在里面找到最大的N个数字。因为是海量的数据文件,所以还是按照以前外部排序那种,每次读文件的一部分,然后一个一个地吐数字,以确保内存可以处理。然后在读文件吐出的一个个数字中,找到最大的N个数字
实现思路:
- 将海量的数据用文件流读入,可以一行一行地读,然后数字一个一个地吐出来
- 先吐出N个数字,建立优先队列,最小的数字优先级最大,可以用Java实现好了的优先队列
- 继续吐数字,如果吐出的数字小于等于优先队列的最小值,则放过
- 如果吐出的数字大于优先队列的最小值,则将优先队列最小的数字(优先级最大)出队,然后入队刚刚吐出来的数字。注意,优先队列里面数字的个数永远保持N个
- 所有的数字都吐完后,优先队列里面的N个数字就是海量数据中最大的N个数字
下面是我的实现代码,比如我们想要在10000个数字里面找到最大的20个数字,数字都是百万级的:
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Random;
/**
* @author LiYang
* @ClassName TopN
* @Description 用优先队列实现TopN问题
* @date 2019/11/13 11:05
*/
public class TopN {
//需要得到的最大数字的数量
//就是TopN中的N
private int topNum;
//用于实现TopN的优先队列
private PriorityQueue<Long> priorityQueue;
/**
* TopN工具类的构造方法
* @param topNum 需要统计的最大数字的个数
*/
public TopN(int topNum) {
this.topNum = topNum;
this.priorityQueue = new PriorityQueue<>();
}
/**
* 计算TopN的N个最大数字
* @param element 海量数字之一
*/
public void countTopN(long element) {
//如果优先队列里面的数字已经有N个了
if (priorityQueue.size() == topNum) {
//查看优先队列中最小数字
long minimum = priorityQueue.peek();
//如果当前数字比优先队列中最小数字要大
if (element > minimum) {
//把优先队列中最小数字出队
priorityQueue.poll();
//让更大的数字入队
priorityQueue.add(element);
}
//如果优先队列里面的数字还未到N个
} else {
//当前数字入队优先队列,并不做出队操作
priorityQueue.add(element);
}
}
/**
* 在文件读完,countTopN操作结束后
* 获取最大N个数字的数组,升序排列
* @return TopN数字数组,升序
*/
public long[] getTopN() {
//TopN的结果数组
long[] result = new long[topNum];
//将TopN的数字全部出队
for (int i = 0; i < result.length; i++) {
//这样操作,结果数组就是升序的
result[i] = priorityQueue.poll();
}
//返回TopN的结果数组
return result;
}
/**
* 测试TopN工具类
* 测试用例是百万级的10000个数字,找出Top20的数字
* @param args
*/
public static void main(String[] args) {
//模拟大文件上的海量数据
long[] bigData = new long[10000];
//随机数类
Random random = new Random();
//生成百万级的"大文件"上的数据
for (int i = 0; i < bigData.length; i++) {
bigData[i] = random.nextInt(10000000);
}
//创建TopN工具类的实例,统计最大的20个数字
TopN topN = new TopN(20);
//将"大文件"上的数据依次"吐出",进行统计
for (int i = 0; i < bigData.length; i++) {
topN.countTopN(bigData[i]);
}
//统计完毕,获取Top20的最大的20个数字
long[] result = topN.getTopN();
//打印统计结果(最大的20个数字的数组)
System.out.println("最大的20个数字:" + Arrays.toString(result));
}
}
运行TopN工具类的main方法,控制台输出从10000个百万级数字中获得的最大的20个数字,测试通过(为了方便查看,控制台输出内容适当做了换行操作):
最大的20个数字:
[9974849, 9977210, 9978159, 9978458, 9978577,
9978882, 9982264, 9983253, 9983472, 9983855,
9986296, 9987833, 9988433, 9989011, 9989025,
9989105, 9989854, 9993490, 9997926, 9999579]