我準備用優先隊列來實現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]