千家信息网

Lucene学习笔记之-核心数据结构PriorityQueue的实现原理

发表于:2025-02-05 作者:千家信息网编辑
千家信息网最后更新 2025年02月05日,Luene的核心应用场景是全文检索。简单来说,就是通过用户输入的关键词来匹配相关文档,然后根据匹配程度返回TopN的查询结果给用户。 这里需要解决的一个核心问题就是如何快速返回TopN的结果,这本质上
千家信息网最后更新 2025年02月05日Lucene学习笔记之-核心数据结构PriorityQueue的实现原理

Luene的核心应用场景是全文检索。简单来说,就是通过用户输入的关键词来匹配相关文档,然后根据匹配程度返回TopN的查询结果给用户。 这里需要解决的一个核心问题就是如何快速返回TopN的结果,这本质上是一个排序的问题。说起排序,我们有很多选择,冒泡,快排,归并...。 这些排序算法在数据量小的时候,不是问题。一旦数据量过大,就成为问题了。

例如对1000万的数组排序:

        Integer[] a = new Integer[10000000];        for(int i=0;i<10000000;i++){            a[i] = (int) (Math.random()*10000000);        }        long start = System.currentTimeMillis();        Arrays.sort(a);        System.out.println((System.currentTimeMillis() - start) +" 毫秒");

在我的电脑耗时需要5秒左右, 这个等待时间对于用户体验来说,就不那么feeling good了。

这时候,该考虑优化了。优化基本上是一个做减法的过程。再回到我们的核心需求: 基于搜索关键词返回TopN的结果。 也就是说,我们只需要TopN的结果有序就可以了。 基于上述需求,我们引入一个新的数据结构: 堆(Heap)。

堆是一种特殊的二叉树。所谓二叉树就是每个节点最多有两个子节点: 最多生二胎,超生不被允许的。

对于二叉树这种树形结构,最核心的关系就是父子节点关系。 定义不同的节点关系,我们就能得到丰富多彩的数据结构,以应对不同场景的业务问题。比如:

规定"子节点不能大于父节点", 我们可以得出根节点是最大的节点, 得到大顶堆。

规定"子节点不能小于父节点", 我们可以得出根节点是最小的节点, 得到小顶堆。

规定"根节点大于左子树,小于右子树;子树亦是如此", 我们得到二叉搜索树;为了使二叉搜索树的左右尽量平衡,我们又得到了"红黑树","AVL树",Treap等不同策略的平衡树。

这些概念性的东西,能理解就OK.

理解了堆的来龙去脉, 我们可能会有点困惑,它并没有直接维护一个有序的结构。 是的,它没有直接维护有序的结构,它是通过删除数据实现排序功能的。理解这一点特别重要。 以大顶堆为例: 由于堆顶是最大的元素,所以我们能确信,对于一个堆: 我们只要不断地删除堆顶的数据,直至空堆,就能得到一个有序的结果。这就是堆排序的思想。

那么如何利用堆实现TopN的有序输出呢? 以搜索的打分作为排序项,我们希望输出得分最高的N个结果。 我们先遍历N个结果,得到有N个元素的小顶堆。由于堆顶的元素最小, 遍历剩下的打分结果,只需要跟堆的根节点对比即可。如果打分结果小于堆的根节点,弃之;如果打分结果大于堆的根节点,删除根节点;然后使用该打分结果更新到堆中。 这样最后这个堆就维护了我们想要的TopN。

例如对1000万的数据,我们给出最大的前100个数,代码如下:

Integer[] a = new Integer[10000000];        for(int i=0;i<10000000;i++){            a[i] = (int) (Math.random()*10000000);        }        long start = System.currentTimeMillis();        PriorityQueue pq = new PriorityQueue(100) {            @Override            protected boolean lessThan(Integer t1, Integer t2) {                return t1 < t2;            }        };        for(int i=0;i<10000000;i++){            pq.insertWithOverflow(a[i]);        }        Integer[] b = new Integer[100];        for(int i=99;i>=0;i--){            b[i] = pq.pop();        }        System.out.println((System.currentTimeMillis() - start) +" 毫秒");        System.out.println(Arrays.asList(b));

这个耗时只需要50多毫秒。 这个性能差距几乎是100倍。可见堆这种数据结构在TopN这个场景下是多么适合。

其实JDK有自己基于堆实现的优先队列PriorityQueue, 为啥Lucene要再造一遍轮子呢?

JDK默认的PriorityQueue是可以自动扩展的,Lucene需要定长的。
JDK默认的PriorityQueue将数据结构封装得比较紧密,而Lucene需要一定的灵活性,比如调整堆顶。

小顶堆是一种二叉树,所以其逻辑结构大致如下:

    1 3    25 8  7 6

如果观察,可以发现这个一个规律,就是第一层只有1个元素;第二层最多有2个元素; 第三层最多有4个元素, 即第N层有2^(n-1)个元素。 这个规律后面有用。

那么怎么编码实现一个堆呢? 最简单的实现方式是基于数组,以Lucene的实现为例,学习一下:

public abstract class PriorityQueue {    private int size;    private final int maxSize;    private final T[] heap;

定义了一个数组。 只需要做如下的规定,那么就能满足對的逻辑结构:

1. heap[0]位空置不用。2. heap[1]为根节点。3. heap[2~3]为第二层,heap[4~7] 为第三层 ... heap[2^n ~ 2^(n+1)-1]为第n+1层

这样,元素在数组的哪个位置,我们就能知道它属于哪一层了。

接下来要解决的问题是:

  1. 如何插入一个元素到堆中?

假设前面有N个元素了, 那么代码很简单

    public final T add(T element) {        ++this.size;        this.heap[this.size] = element;        this.upHeap(this.size);        return this.heap[1];    }

两步走: s1 将元素添加到尾巴上。 s2: 由于这个元素有可能比其父节点小,所以递归地跟其父节点比较,换位置即可,这里有点冒泡的感觉。即想象把乒乓球按入水中,松手后就会上浮。

  1. 如何从堆中删除一个元素?
   public final T pop() {        if (this.size > 0) {            T result = this.heap[1];            this.heap[1] = this.heap[this.size];            this.heap[this.size] = null;            --this.size;            this.downHeap(1);            return result;        } else {            return null;        }    }

两步走: s1: 用数组尾巴上的元素覆盖跟节点元素。 s2: 由于这个元素是否能胜任根节点这个位置还不确定,因此需要跟两个子节点比较,调整位置。这里有丝下沉的感觉。即想象把铁球丢入水中,自己就沉了下去。

这里,堆的插入和删除操作还是思路还是比较轻奇的,值得好好揣摩一番。

在Lucene中,PriorityQueue有那些应用场景呢?

  1. HitQueue, 搜索打分的核心。
  2. FieldValueHitQueue, 按字段排序的核心。
    ....

总之,该数据结构在Lucene中有30~40个子类,应用十分广泛。了解其实现机制,对于了解其他的功能大有裨益。

节点 元素 结果 数据 结构 排序 核心 就是 问题 数据结构 有序 数组 搜索 位置 场景 不同 最大 用户 子树 应用 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 珠海网络安全工程师简单易学 电脑蒲公英服务器删了还能下载吗 数据库需要放在dmz区域吗 做软件开发笔记本电脑推荐 网络安全人生规划 小商品零售店数据库 计算机网络技术专业相关 全球疫情数据可视化数据库 华珍峰网络技术工作室 软件开发 甲方要源代码 软件开发英文怎么讲 数据库与程序的安装 软件开发培训班上班吗 人民银行西安分行网络安全周 河北省冀州市网络安全宣传 数据库弹性云服务器指哪个服务器 网络安全湖北警官学院 人大金仓数据库授权文件丢了 boss服务器出了问题吗 此数据库缺少完整数据库备份 全球疫情数据可视化数据库 js数据库分页显示 郑州工商网络安全等级测评项目 简述网络安全包括哪 服务器管理器没有了 计算机三级数据库 题型 用友cloud和云服务器区别 军人网络安全知识大全 适合做软路由服务器 计算机网络技术都包括哪些
0