理解WPF源码之PriorityQueue<T>

0 前言

在理解WPF的 Dispatcher 的过程中,在其源码中发现一行代码:

 private PriorityQueue<DispatcherOperation> _queue;

从命名的字面意思理解PriorityQueue<DispatcherOperation>是一个DispatcherOperation对象的优先级队列。虽然PriorityQueue<T>类被标记为internal,但感觉这个数据结构还是值得学习一下。

1 结构分析

1.1 UML 图

优先级队列主要涉及到三个类:PriorityQueue<T>PriorityItem<T>PriorityChain<T>。为了能够从对它们有一个整体上的理解,我简单画了一下UML类图。

PriorityQueue结构图
不难发现,PriorityQueue<T>内部由链表实现(头元素引用为_head,尾元素为_tail),链表的基本元素为PriorityItem<T>

PriorityItem<T>看起来有些复杂,其内部有两对元素引用{SequentialPrevSequentialNext} 和{PriorityPrevPriorityNext} ,以及一个PriorityChain<T>类型的属性 Chain

从字面意思来看,PriorityItem<T>元素似乎同时位于两个链表上,一个是顺序链表,一个是优先级链表。到底是怎样的呢?于是我继续读代码,简单画了下优先级队列的结构图。

1.2 简化结构图

简化结构图
在简化结构图中:

  • Sp:即SequentialPrev,表示元素在顺序链表中前驱节点;
  • Sn:即SequentialNext,表示元素在顺序链表中后继节点;
  • Pp:即PriorityPrev,表示元素在优先级链表中前驱节点;
  • Pn:即PriorityNext,表示元素在优先级链表中后继节点。

结合UML类图、简化结构图和PriorityQueue<T>类中的字段

private SortedList<int, PriorityChain<T>> _priorityChains;

可以得出结论:

  • 优先级队列以顺序链表为骨架的,构建并维护多条优先级关系链表;
  • 多条优先级关系链表通过排序的优先级链表维护。

2 代码分析

泛型类PriorityQueue<T>用于实现一个优先级队列,该队列具有以下功能:

  • Enqueue(入队):将具有特定优先级的数据元素添加到队列中;
  • Dequeue(出队):从队列中返回并移除最高优先级的元素;
  • RemoveItem(删除):从队列中删除元素;
  • Peek(查看队首元素):返回但不移除队列中最高优先级的元素;
  • ChangeItemPriority(改变元素优先级):更改队列中元素的优先级。

PriorityQueue<T>类使用了几个私有字段实现这些功能:

  • _priorityChains:用于存储按优先级排序的链表,类型为SortedList<int, PriorityChain<T>>
  • _cacheReusableChains:用于缓存可重用的链表,类型为Stack<PriorityChain<T>>;
  • _head:队首元素,类型为PriorityItem<T>;
  • _tail:队尾元素,类型为PriorityItem<T>;
  • _count:表示队列中元素(PriorityItem<T>)数量。

2.2 入队

入队(Enqueue),是将具有特定优先级的数据元素添加到队列中。思路:首先将T类型的数据包装成一个PriorityItem<T>并插入到顺序链表的尾部,然后再构建将元素的优先级链表关系。

/// <summary>
/// 将指定数据入队到优先级队列
/// </summary>
/// <param name="priority"></param>
/// <param name="data"></param>
/// <returns></returns>
public PriorityItem<T> Enqueue(DispatcherPriority priority, T data)
{
    // 1. 获取优先级链表
    PriorityChain<T> chain = GetChain(priority);

    // 2. 将指定数据包装成链表的元素类型
    PriorityItem<T> priorityItem = new PriorityItem<T>(data);

    // 3. 将元素插入到顺序链表的尾部 
    InsertItemInSequentialChain(priorityItem, _tail);

    // 4. 将元素插入指定优先级链表的尾部
    InsertItemInPriorityChain(priorityItem, chain, chain.Tail);
    return priorityItem;
}

T类型的数据包装成一个PriorityItem<T>并插入到顺序链表的尾部,这一步很简单和普通链表的插入逻辑是一致的,按下不表。

关于构建元素的优先级链表关系,其构建逻辑如下:

  • 在排序链表集合_priorityChains中,获取指定优先级priority的关系链表;
  • 若1没找到,则需要新构建一条关系链表实例并添加到排序链表集合_priorityChains中。首先尝试从缓存栈_cacheReusableChains中弹出一个PriorityChain<T>实例;若缓存栈为空,则新建一个此实例。

关于缓存栈_cacheReusableChains

  • 目的是减少PriorityChain<T>实例构建次数;
  • 缓存栈最大数量为10;
  • 当某一条PriorityChain<T>的元素为0且栈大小不超过10,则不会删除优先级链表,而是将其放入缓存栈;
  • 当需要构建新的优先级priority的关系链表时,首先从缓存栈中获取,而不是每次都要新建实例。

2.3 出队

出队(Dequeue) ,即从队列中返回并移除最高优先级的元素;实现思路如下:

  1. 获取出队的对象:由于排序链表集合_priorityChains是有序的,最后一个元素就是最高优先级,即为需要出队的对象;
  2. 删除元素
  3. 返回出队对象
/// <summary>
/// 从优先级队列出队,并获取数据
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public T Dequeue()
{
    int count = _priorityChains.Count;
    if (count <= 0)
    {
        throw new InvalidOperationException();
    }
    // 1. 获取最高优先级的队首元素
    PriorityItem<T> head = _priorityChains.Values[count - 1].Head;

    // 2. 从队列移除队首元素
    RemoveItem(head);

    // 3. 返回队首元素的数据
    return head.Data;
}

2.4 删除

删除(RemoveItem):从队列中删除元素。思路:先从相应的优先级链表中删除关系,再从顺序连表中删除实例。

/// <summary>
/// 从优先级队列移除元素
/// </summary>
/// <param name="item"></param>
public void RemoveItem(PriorityItem<T> item)
{
    PriorityChain<T> chain = item.Chain;

    // 1. 从优先级链表中删除指定元素(删除优先级关系)
    RemoveItemFromPriorityChain(item);

    // 2. 从顺序链表中删除指定元素(彻底删除)
    RemoveItemFromSequentialChain(item);
}

2.5 查看队首元素

查看队首元素(Peek),返回但不移除队列中最高优先级的元素。

/// <summary>
/// 查询优先级队列的队首元素的数据
/// </summary>
/// <returns></returns>
public T Peek()
{
    T obj = default(T);
    int count = _priorityChains.Count;
    if (count > 0)
    {
        obj = _priorityChains.Values[count - 1].Head.Data; // 获取最高优先级的队首元素的数据
    }
    return obj;
}

2.6 改变元素优先级

改变元素优先级(ChangeItemPriority),更改队列中元素的优先级。

/// <summary>
/// 改变指定元素的优先级
/// </summary>
/// <param name="item"></param>
/// <param name="priority"></param>
public void ChangeItemPriority(PriorityItem<T> item, DispatcherPriority priority)
{
    // 1. 从优先级链表中删除指定元素
    RemoveItemFromPriorityChain(item);

    // 2. 获取指定优先级的优先级链表
    PriorityChain<T> chain = GetChain(priority);

    // 3. 插入元素到指定的优先级队列
    InsertItemInPriorityChain(item, chain);
}

3 源码及注释

namespace System.Windows.Threading
{
    internal class PriorityQueue<T>
    {
        private SortedList<int, PriorityChain<T>> _priorityChains;  // 排序的优先级链表
        private Stack<PriorityChain<T>> _cacheReusableChains;       // 优先级链表缓存栈
        private PriorityItem<T> _head;                              // 顺序链表的头元素(不是队首元素)
        private PriorityItem<T> _tail;                              // 顺序链表的尾元素(不是队尾元素)
        private int _count;

        public PriorityQueue()
        {
            _priorityChains = new SortedList<int, PriorityChain<T>>();
            _cacheReusableChains = new Stack<PriorityChain<T>>(10);
            _head = _tail = (PriorityItem<T>)null;
            _count = 0;
        }

        /// <summary>
        /// 优先级队列的最大优先级
        /// </summary>
        public DispatcherPriority MaxPriority
        {
            get
            {
                int count = _priorityChains.Count;
                return count > 0 ? (DispatcherPriority)_priorityChains.Keys[count - 1] : DispatcherPriority.Invalid;
            }
        }

        /// <summary>
        /// 将指定数据入队到优先级队列
        /// </summary>
        /// <param name="priority"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public PriorityItem<T> Enqueue(DispatcherPriority priority, T data)
        {
            // 1. 获取优先级链表
            PriorityChain<T> chain = GetChain(priority);

            // 2. 将指定数据包装成链表的元素类型
            PriorityItem<T> priorityItem = new PriorityItem<T>(data);

            // 3. 将元素插入到顺序链表的尾部 
            InsertItemInSequentialChain(priorityItem, _tail);

            // 4. 将元素插入指定优先级链表的尾部
            InsertItemInPriorityChain(priorityItem, chain, chain.Tail);
            return priorityItem;
        }

        /// <summary>
        /// 从优先级队列出队,并获取数据
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public T Dequeue()
        {
            int count = _priorityChains.Count;
            if (count <= 0)
            {
                throw new InvalidOperationException();
            }
            // 1. 获取最高优先级的队首元素
            PriorityItem<T> head = _priorityChains.Values[count - 1].Head; 

            // 2. 从队列移除队首元素
            RemoveItem(head);

            // 3. 返回队首元素的数据
            return head.Data;
        }

        /// <summary>
        /// 查询优先级队列的队首元素的数据
        /// </summary>
        /// <returns></returns>
        public T Peek()
        {
            T obj = default(T);
            int count = _priorityChains.Count;
            if (count > 0)
            {
                obj = _priorityChains.Values[count - 1].Head.Data; // 获取最高优先级的队首元素的数据
            }
            return obj;
        }

        /// <summary>
        /// 从优先级队列移除元素
        /// </summary>
        /// <param name="item"></param>
        public void RemoveItem(PriorityItem<T> item)
        {
            PriorityChain<T> chain = item.Chain;

            // 1. 从优先级链表中删除指定元素(删除优先级关系)
            RemoveItemFromPriorityChain(item);

            // 2. 从顺序链表中删除指定元素(彻底删除)
            RemoveItemFromSequentialChain(item);
        }

        /// <summary>
        /// 改变指定元素的优先级
        /// </summary>
        /// <param name="item"></param>
        /// <param name="priority"></param>
        public void ChangeItemPriority(PriorityItem<T> item, DispatcherPriority priority)
        {
            // 1. 从优先级链表中删除指定元素
            RemoveItemFromPriorityChain(item);

            // 2. 获取指定优先级的优先级链表
            PriorityChain<T> chain = GetChain(priority);

            // 3. 插入元素到指定的优先级队列
            InsertItemInPriorityChain(item, chain);
        }

        /// <summary>
        /// 获取指定优先级的优先级链表
        /// </summary>
        /// <param name="priority">指定的优先级</param>
        /// <returns></returns>
        private PriorityChain<T> GetChain(DispatcherPriority priority)
        {
            PriorityChain<T> chain = (PriorityChain<T>)null;

            // 获取优先级链表总条数,若存在优先级链表,则获取链表
            int count = _priorityChains.Count;
            if (count > 0)
            {
                if (priority == (DispatcherPriority)_priorityChains.Keys[0])
                {
                    chain = _priorityChains.Values[0];
                }
                else if (priority == (DispatcherPriority)_priorityChains.Keys[count - 1])
                {
                    chain = _priorityChains.Values[count - 1];
                }
                else if (priority > (DispatcherPriority)_priorityChains.Keys[0] &&
                         priority < (DispatcherPriority)_priorityChains.Keys[count - 1])
                {
                    _priorityChains.TryGetValue((int)priority, out chain);
                }
            }

            // 若没找到优先级链表
            // 1. 从缓存栈取一条链表,修改其优先级到指定优先级,返回
            // 2. 若缓存栈中也没有,则新创建一条优先级链表实例
            // 3. 将新的优先级链表(不论是从缓存栈获取还是新建的)添加到排序的优先级链表字典中
            // 4. 返回结果
            if (chain == null)
            {
                if (_cacheReusableChains.Count > 0)
                {
                    chain = _cacheReusableChains.Pop();
                    chain.Priority = priority;
                }
                else
                {
                    chain = new PriorityChain<T>(priority);
                }
                _priorityChains.Add((int)priority, chain);
            }
            return chain;
        }

        /// <summary>
        /// 插入元素到指定的优先级队列
        /// </summary>
        /// <param name="item"></param>
        /// <param name="chain"></param>
        private void InsertItemInPriorityChain(PriorityItem<T> item, PriorityChain<T> chain)
        {
            if (chain.Head == null)
            {
                InsertItemInPriorityChain(item, chain, (PriorityItem<T>)null);
            }
            else
            {
                PriorityItem<T> sequentialPrev = item.SequentialPrev;

                // 迭代(前向搜索)的停止条件
                // case 1: 找到 item 所在顺序链表的头节点
                // case 2: 找到一个节点,其所在的优先级链表就是要插入的优先级链表
                while (sequentialPrev != null && sequentialPrev.Chain != chain)
                {
                    sequentialPrev = sequentialPrev.SequentialPrev;
                }

                // 将元素插入到指定的优先级链表的指定元素之后
                InsertItemInPriorityChain(item, chain, sequentialPrev);
            }
        }

        /// <summary>
        /// 将元素插入到指定的优先级链表的指定元素之后
        /// </summary>
        /// <param name="item">待插入的元素</param>
        /// <param name="chain">优先级链表</param>
        /// <param name="after"></param>
        internal void InsertItemInPriorityChain(
          PriorityItem<T> item,
          PriorityChain<T> chain,
          PriorityItem<T> after)
        {
            // 将待插入的元素加入到指定的优先级链表
            item.Chain = chain;

            // 若 after 为 null ,则插入到 head 前面
            // 若 after 非 null ,则插入到 after 后面
            if (after == null)
            {
                if (chain.Head != null)
                {
                    chain.Head.PriorityPrev = item; // 1. 将 head 的前驱(Prev)设为 item
                    item.PriorityNext = chain.Head; // 2. 将 item 的后继(Next)设为 head
                    chain.Head = item;              // 3. 将 item 设为头节点
                }
                else
                {
                    chain.Head = chain.Tail = item;
                }
            }
            else
            {
                // 将 item 的前驱(Prev)设置为 after (即表示将 item 插入到 after 后面)
                item.PriorityPrev = after;

                // 若 after 存在后继(Next), 则执行双向链表插入元素的常规逻辑
                // 若 after 无后继,则将 after 的后继设为 item并将_tail设为尾节点
                if (after.PriorityNext != null)
                {
                    item.PriorityNext = after.PriorityNext; // 1.将 item 的后继(Next)设置为 after 的后继
                    after.PriorityNext.PriorityPrev = item; // 2.将 after 的后继的前驱设为 item
                    after.PriorityNext = item;              // 3.将 after 的后继设为 item
                }
                else
                {
                    after.PriorityNext = item;
                    chain.Tail = item;
                }
            }
            ++chain.Count;// 优先级链表
        }

        /// <summary>
        /// 从优先级链表中删除指定元素
        /// </summary>
        /// <param name="item"></param>
        private void RemoveItemFromPriorityChain(PriorityItem<T> item)
        {
            // 若有前驱(Prev),则执行链表删除元素的常规逻辑
            // 若无前驱,则把后继(Next)设为优先级链表的头节点
            if (item.PriorityPrev != null)
            {
                item.PriorityPrev.PriorityNext = item.PriorityNext; // 把前一个的 Next 置为 item 的 Next
            }
            else
            {
                item.Chain.Head = item.PriorityNext;
            }

            // 若有后继(Next),则把 item 的前驱(Prev)设置为后继的前驱
            // 若无后继,则把 item 的前驱(Prev)设为优先级链表的尾节点
            if (item.PriorityNext != null)
            {
                item.PriorityNext.PriorityPrev = item.PriorityPrev;
            }
            else
            {
                item.Chain.Tail = item.PriorityPrev;
            }

            // 将 item 的前驱和后继都置为null,并将优先级链表数量减一
            item.PriorityPrev = item.PriorityNext = (PriorityItem<T>)null;
            --item.Chain.Count;

            // 若元素的优先级链表中节点数量为 0(即优先级链表无元素)
            if (item.Chain.Count == 0)
            {
                // 若 item 所在的优先级链表的优先级是最高优先级,则删最后一个
                // 否则,按通过字典的键删之
                if (item.Chain.Priority == (DispatcherPriority)_priorityChains.Keys[_priorityChains.Count - 1])
                {
                    _priorityChains.RemoveAt(_priorityChains.Count - 1);
                }
                else
                {
                    _priorityChains.Remove((int)item.Chain.Priority);
                }

                // 若缓存容量小于10,则将 item 所在优先级链表压入链表缓存栈
                if (_cacheReusableChains.Count < 10)
                {
                    item.Chain.Priority = DispatcherPriority.Invalid;
                    _cacheReusableChains.Push(item.Chain);
                }
            }

            // 将 item 指向的优先级队列的引用置为null
            item.Chain = (PriorityChain<T>)null;
        }

        /// <summary>
        /// 插入一个元素到顺序链表
        /// </summary>
        /// <param name="item">待插入的元素</param>
        /// <param name="after"></param>
        internal void InsertItemInSequentialChain(PriorityItem<T> item, PriorityItem<T> after)
        {
            // 若 after 为 null ,则插入到 head 前面
            // 若 after 非 null ,则插入到 after 后面
            if (after == null)
            {
                // 若存在头节点,将 item 插入到 head 前面
                // 若头节点为 null,则待插入的元素作为头节点和尾节点
                if (_head != null)
                {
                    _head.SequentialPrev = item; // 1. 将 head 的前驱(Prev)设为 item
                    item.SequentialNext = _head; // 2. 将 item 的后继(Next)设为 head
                    _head = item;                // 3. 将 item 设为头节点
                }
                else
                {
                    _head = _tail = item;
                }
            }
            else
            {
                // 将 item 的前驱(Prev)设置为 after (即表示将 item 插入到 after 后面)
                item.SequentialPrev = after;

                // 若 after 存在后继(Next), 则执行双向链表插入元素的常规逻辑
                // 若 after 无后继,则将 after 的后继设为 item并将_tail设为尾节点
                if (after.SequentialNext != null)
                {
                    item.SequentialNext = after.SequentialNext; // 1.将 item 的后继(Next)设置为 after 的后继
                    after.SequentialNext.SequentialPrev = item; // 2.将 after 的后继的前驱设为 item
                    after.SequentialNext = item;                // 3.将 after 的后继设为 item
                }
                else
                {
                    after.SequentialNext = item;
                    _tail = item;
                }
            }
            ++_count;// 顺序链表数量加一
        }

        /// <summary>
        /// 从顺序链表中删除指定元素
        /// </summary>
        /// <param name="item"></param>
        private void RemoveItemFromSequentialChain(PriorityItem<T> item)
        {
            // 若有前驱(Prev),则把前一个的Next置位item的Next【链表删除元素的常规逻辑】
            // 若无前驱,则把后继(Next)设为头节点
            if (item.SequentialPrev != null)
            {
                item.SequentialPrev.SequentialNext = item.SequentialNext;
            }
            else
            {
                _head = item.SequentialNext;
            }

            // 若有后继(Next),则把item的前驱(Prev)设置为后继的前驱
            // 若无后继,则把item的前驱(Prev)设为尾节点
            if (item.SequentialNext != null)
            {
                item.SequentialNext.SequentialPrev = item.SequentialPrev;
            }
            else
            {
                _tail = item.SequentialPrev;
            }

            // 将item的前驱和后继都置为null,并将队列数量减一
            item.SequentialPrev = item.SequentialNext = (PriorityItem<T>)null;
            --_count;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值