Queue底层源码解析

Queue简介

实现了一个先进先出的集合,提供了在队列中进行元素的添加和移除的功能。底层实现依赖于循环数组。

Queue源码分析

1、基本结构

  • _array:使用泛型数组存储数据
  • _head:表示队列的头部索引(即下一个要出队的元素位置)
  • _tail:表示队列的尾部索引(即下一个要入队的元素位置)
  • _size:表示队列中当前元素的数量
  • _version:用于跟踪队列的修改版本,以支持迭代器的正确操作
  • _defaultCapacity:初始容量,默认为4
public class Queue<T> : IEnumerable<T>, ICollection, IReadOnlyCollection<T>
{
    private T[] _array;
    private int _head;       // The index of the head of the queue.
    private int _tail;       // The index of the tail of the queue.
    private int _size;       // Number of elements.
    private int _version;

    private const int _defaultCapacity = 4;

    public Queue()
    {
        _array = Array.Empty<T>();
    }

    public Queue(int capacity)
    {
        if (capacity < 0)
            throw new ArgumentOutOfRangeException(nameof(capacity));
        _array = new T[capacity];
    }

    public Queue(IEnumerable<T> collection)
    {
        if (collection == null)
            throw new ArgumentNullException(nameof(collection));

        _array = new T[_defaultCapacity];
        foreach (var item in collection)
        {
            Enqueue(item);
        }
    }
}

2、动态扩展机制

  • 当队列中元素数量达到当前数组的容量时,会自动扩展数组的大小,通常是将当前容量翻倍,以减少频繁的内存分配操作
public void Enqueue(T item)
{
    if (_size == _array.Length)
    {
		// 计算新的容量,通常是当前容量的两倍
		//在计算新容量时,直接使用 array.Length * 2 可能会导致溢出,尤其是当数组的长度接近 int.MaxValue 时。为了避免这种情况,使用 long 类型进行计算。
		//通过 (long)array.Length * 200L / 100L,先将 array.Length 转换为 long,然后再进行乘法和除法运算,确保中间计算过程不会溢出。
        int newCapacity = (int)((long)_array.Length * 200L / 100L);
		// 确保新的容量至少比当前容量大 _defaultCapacity
        if (newCapacity < _array.Length + _defaultCapacity)
        {
            newCapacity = _array.Length + _defaultCapacity;
        }
        SetCapacity(newCapacity);
    }

    _array[_tail] = item;
	// 更新队列尾部索引,如果到达数组末尾则循环到数组开头
	//循环数组用于实现固定大小的队列。它的一个关键特性是,当到达数组的末尾时,继续从数组的起始位置进行操作,从而实现逻辑上的连续存储。
    _tail = (_tail + 1) % _array.Length;
    _size++;
    _version++;
}

private void SetCapacity(int capacity)
{
    T[] newArray = new T[capacity];
    if (_size > 0)
    {
		// 如果队列中的元素是连续存储的(即 head 在 tail 之前)
        if (_head < _tail)
        {
			// 直接将从 head 到 tail 之间的元素复制到新数组中
            Array.Copy(_array, _head, newArray, 0, _size);
        }
        else
        {
			// 如果队列中的元素是分段存储的(即 head 在 tail 之后)
            // 先复制从 head 到数组末尾的元素
            Array.Copy(_array, _head, newArray, 0, _array.Length - _head);
			 // 再复制从数组开始到 tail 的元素
            Array.Copy(_array, 0, newArray, _array.Length - _head, _tail);
        }
    }

    _array = newArray;
	// 更新 head 为 0,因为新数组中的第一个元素应该在索引 0 位置
    _head = 0;
	// 更新 tail,如果队列已满则设置为 0,否则设置为元素数量 _size
    _tail = (_size == capacity) ? 0 : _size;
}

3、队列的操作方法

  • Dequeue:移除并返回队列的头部元素。

    public T Dequeue()
    {
        if (_size == 0)
            throw new InvalidOperationException("Queue empty");
        
        T removed = _array[_head];
        _array[_head] = default!;
        _head = (_head + 1) % _array.Length;
        _size--;
        _version++;
        return removed;
    }
    
  • Peek:返回队列的头部元素但不移除。

    public T Peek()
    {
        if (_size == 0)
            throw new InvalidOperationException("Queue empty");
        return _array[_head];
    }
    
  • Contains:检查队列中是否包含指定元素

    public bool Contains(T item)
    {
        int index = _head;
        int count = _size;
        EqualityComparer<T> c = EqualityComparer<T>.Default;
    
        while (count-- > 0)
        {
            if (item == null)
            {
                if (_array[index] == null)
                    return true;
            }
            else if (_array[index] != null && c.Equals(_array[index], item))
            {
                return true;
            }
            index = (index + 1) % _array.Length;
        }
    
        return false;
    }
    
  • Clear:清空队列中的所有元素。

    public void Clear()
    {
        if (_size > 0)
        {
            if (_head < _tail)
            {
                Array.Clear(_array, _head, _size);
            }
            else
            {
                Array.Clear(_array, _head, _array.Length - _head);
                Array.Clear(_array, 0, _tail);
            }
        }
    
        _head = 0;
        _tail = 0;
        _size = 0;
        _version++;
    }
    
  • ToArray:将队列中的元素复制到一个新数组。

    public T[] ToArray()
    {
        T[] arr = new T[_size];
        if (_size == 0)
            return arr;
    
        if (_head < _tail)
        {
            Array.Copy(_array, _head, arr, 0, _size);
        }
        else
        {
            Array.Copy(_array, _head, arr, 0, _array.Length - _head);
            Array.Copy(_array, 0, arr, _array.Length - _head, _tail);
        }
        return arr;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值