java 优先级队列 PriorityQueue可实现自动排序功能
数据结构:小顶堆
源码和原理: https://baijiahao.baidu.com/s?id=1665383380422326763&wfr=spider&for=pc
1. 海量数据中筛选出最大或最小的10个数
package com.eaglefly.common.service;
import org.junit.Test;
import java.io.*;
import java.util.PriorityQueue;
import java.util.Random;
/**
* 计算海量数据中的top K
*
* 在topkdata.txt 文件中模拟海量数据
* 使用堆排序筛选top K个数据
*/
public class TopKDemo {
private final Random random = new Random();
/**
* 存储1000万条数据的文件
*/
private final File file = new File("topkdata.txt");
/**
* java中有「优先级队列」来做为「小顶堆」来使用
*/
private final PriorityQueue priorityQueue = new PriorityQueue<>(10);
@Test
public void computeTopK() {
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try {
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
//todo 核心方法
addToTopKQueue(Long.valueOf(line));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// close()
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//todo 输出Long 类型的数据的最大值,便于判断top 10 的数据是否正确
System.out.println("Long.MAX_VALUE = " + Long.MAX_VALUE);
//todo 输出 PriorityQueue队列中的数据
Long target;
while ((target = (Long) priorityQueue.poll()) != null) {
System.out.println("target = " + target);
}
}
}
/**
* init方法仅运行一次即可,是为准备模拟数据
*/
@Test
public void init() {
long start = System.currentTimeMillis();
System.out.println("init");
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(file, true);
//todo 先用1000万数据,多了电脑可能受不了
for (int i = 0; i < 10000000; i++) {
fileWriter.write(String.valueOf(random.nextLong()) + System.lineSeparator());
}
//todo 写入10个接近long的最大值的数,便于取出是验证正确结果
for (int i = 0; i < 10; i++) {
fileWriter.write(String.valueOf(Long.MAX_VALUE - i) + System.lineSeparator());
}
//flush()
fileWriter.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileWriter != null) {
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("用时:" + (System.currentTimeMillis() - start));
}
}
/**
* 判断遍历到的数字是否比小顶堆中堆顶的数字要大:
* 1)如果小的话继续向后遍历;
* 2)如果大的话则将堆顶的数字弹出队列,并将该数字添加到堆当中,并重新调整小顶堆。
*
* @param target 遍历海量数据中的某个数字
*/
public void addToTopKQueue(Long target) {
if (priorityQueue.size() < 10) {
priorityQueue.add(target);
} else {
// 找到堆顶元素的值
Long head = (Long) priorityQueue.peek();
if (target > head) {
priorityQueue.poll();
priorityQueue.add(target);
}
}
}
}
2. 海量数据中筛选出第K最大或最小的数据
public int findKthLargest(int[] nums, int k){
Queue<Integer> priorityQueue = new PriorityQueue<>();
for(int num : nums){
queue.add(num);
// 当queue的大小超过K的时候,则弹出堆顶的最小元素
if(queue.size() > k){
queue.poll();
}
}
// 输出大小为K的队列中队首的元素值(也就是堆顶的元素)
return queue.peek();
}