目录
一、Queue<T>集合的概述
Queue表示先入先出的对象集合,元素值可重复。常用Queue<T>泛型类型。
该类将泛型队列实现为循环数组。存储在Queue<T>中的对象在一端插入,从另一端删除。队列Queue和堆栈Stack在需要临时存储信息时很有用;也就是说,您可能希望在检索元素的值之后丢弃它。如果需要按照存储在集合中的顺序访问信息,请使用Queue<T>。如果需要以相反的顺序访问信息,则使用Stack<T>。如果需要从多个线程并发访问集合,则使用ConcurrentQueue<T>或ConcurrentStack<T>。
可以对Queue<T>及其元素执行三种主要操作:
1、Enqueue方法将一个元素添加到Queue<T>的末尾。
2、Dequeue方法从Queue<T>开始移除最先插入的元素。
3、Peek方法返回位于Queue<T>开头的最先插入的元素,但不会将其从Queue<T>中移除。
Queue<T>的容量是Queue<T>可以容纳的元素个数。当元素被添加到Queue<T>时,通过重新分配内部数组,容量会根据需要自动增加。可以通过调用TrimExcess()方法来减少容量。
Queue<T> 接受 null 作为引用类型的有效值,并允许重复元素。
二、Queue<T>集合的构造函数
Queue<T>的构造函数,有以下三个。
Queue<T>():初始化Queue<T>类的新实例,该实例包含从指定集合复制的元素,并且有足够的容量容纳复制的元素数量。
Queue<T>(IEnumerable<T> collections):初始化Queue<T>类的新实例,该实例包含从指定集合复制的元素,并且有足够的容量容纳复制的元素数量。
Queue<T>(Int32 count):初始化Queue<T>类的新实例,该实例为空并具有指定的初始容量。
三、Queue<T>集合的常用属性
Queue<T>的属性,只有一个:
Count:用于获取Queue<T>集合内部的元素个数。
四、Queue<T>集合的常用方法
Clear():将集合中的元素清空,全部删除。
Contain(T t):判断集合中是否包含某个元素。
CopyTo(T[] array , Int32 index):从指定的数组索引开始,将Queue<T>元素复制到现有的一维数组中。
Enqueue(T object):将对象object添加到Queue<T>的末尾。
Dequeue():移除并返回Queue<T>开头的对象。
Peek():返回Queue<T>开头的对象,但不删除它。
EnsureCapacity(Int32 capacity):确保此队列的容量至少为指定的capacity。如果当前容量小于capacity,则依次增加到当前容量的两倍,直到至少达到指定capacity。
Equals(Object object):确定指定object是否等于当前对象。
GetEnumerator():返回一个枚举数,该枚举数遍历Queue<T>。
GetHashCode():获取默认的哈希值。
MemberwiseClone():创建当前对象的浅拷贝。
ToArray():将Queue<T>集合中的元素,复制到另一个新数组中。
TimExcess():当数量少于当前容量的90%时,将Queue<T>集合的容量,调整为实际元素的数量。
TryDequeue(T result):删除Queue<T>开头的对象,并将其复制到result参数。返回布尔变量,用于指示是否删除成功。
TryPeek():返回一个值,该值指示Queue<T>的开头是否存在对象,如果存在,则将其复制到result参数。该对象未从Queue<T>中移除。
五、Queue<T>集合的实例代码
下面的代码示例演示了Queue<T>泛型类的几个方法。代码示例创建一个具有默认容量的字符串队列,并使用Enqueue方法对五个字符串进行排队。枚举队列的元素,这不会改变队列的状态。Dequeue方法用于将第一个字符串从队列中取出。Peek方法用于查看队列中的下一个项目,然后使用Dequeue方法将其从队列中取出。
ToArray方法用于创建数组并将队列元素复制到其中,然后将该数组传递给queue <T>构造函数,该构造函数接受IEnumerable<T>,从而创建队列的副本。显示副本的元素。
创建一个两倍于队列大小的数组,并使用CopyTo方法复制从数组中间开始的数组元素。再次使用Queue<T>构造函数来创建队列的第二个副本,该队列在开头包含三个null元素。
Contains方法用于显示字符串“four”位于队列的第一个副本中,之后Clear方法清除该副本,Count属性显示队列为空。
using System;
using System.Collections.Generic;
class Example
{
public static void Main()
{
Queue<string> numbers = new Queue<string>();
numbers.Enqueue("one");
numbers.Enqueue("two");
numbers.Enqueue("three");
numbers.Enqueue("four");
numbers.Enqueue("five");
// A queue can be enumerated without disturbing its contents.
foreach( string number in numbers )
{
Console.WriteLine(number);
}
Console.WriteLine("\nDequeuing '{0}'", numbers.Dequeue());
Console.WriteLine("Peek at next item to dequeue: {0}",
numbers.Peek());
Console.WriteLine("Dequeuing '{0}'", numbers.Dequeue());
// Create a copy of the queue, using the ToArray method and the
// constructor that accepts an IEnumerable<T>.
Queue<string> queueCopy = new Queue<string>(numbers.ToArray());
Console.WriteLine("\nContents of the first copy:");
foreach( string number in queueCopy )
{
Console.WriteLine(number);
}
// Create an array twice the size of the queue and copy the
// elements of the queue, starting at the middle of the
// array.
string[] array2 = new string[numbers.Count * 2];
numbers.CopyTo(array2, numbers.Count);
// Create a second queue, using the constructor that accepts an
// IEnumerable(Of T).
Queue<string> queueCopy2 = new Queue<string>(array2);
Console.WriteLine("\nContents of the second copy, with duplicates and nulls:");
foreach( string number in queueCopy2 )
{
Console.WriteLine(number);
}
Console.WriteLine("\nqueueCopy.Contains(\"four\") = {0}",
queueCopy.Contains("four"));
Console.WriteLine("\nqueueCopy.Clear()");
queueCopy.Clear();
Console.WriteLine("\nqueueCopy.Count = {0}", queueCopy.Count);
}
}
/* This code example produces the following output:
one
two
three
four
five
Dequeuing 'one'
Peek at next item to dequeue: two
Dequeuing 'two'
Contents of the first copy:
three
four
five
Contents of the second copy, with duplicates and nulls:
three
four
five
queueCopy.Contains("four") = True
queueCopy.Clear()
queueCopy.Count = 0
*/
六、Queue<T>集合的源码分析
Enqueue中,是将数据保存到一个数组中,数组的初始容量为0。在插入第一个数据时,进行扩容,此时数据的容量个数为4。
6.1、Enqueue(T object)方法的源码分析
下列代码是Enqueue(T object)方法的源码。根据源码可以得到以下几点信息:
1、在插入新数据时,是先判断是否有多余空间,如果有,则直接插入,如果没有则先扩容再插入。
2、扩容时,一般是将容量扩大为当前容量的2倍。但是容量的增加数不能小于4,如果小于4则直接扩容4个。
3、Enqueue<T>集合在初始化时,默认的容量数为0,只有向集合中添加第一个元素的时候,才进行容量初始化,初始容量为4。
[__DynamicallyInvokable]
public void Enqueue(T item)
{
//先判断集合是否有多余空间, 如果没有多余空间,则进行扩容操作,如果有多余空间,则跳过
if (_size == _array.Length)
{
//扩容时,一般是将容量扩大为当前容量的2倍
int num = (int)((long)_array.Length * 200L / 100);
容量的新增数不能小于4,否则容量直接增加4
if (num < _array.Length + 4)
{
num = _array.Length + 4;
}
SetCapacity(num);
}
_array[_tail] = item;
_tail = (_tail + 1) % _array.Length;
_size++;
_version++;
}
6.2、Dequeue()方法的源码分析
下列代码是Dequeue(T object)方法的源码。根据源码可以得到以下几点信息:
1、先判断当前集合中是否含有元素,如果不含有元素,报错。
2、创建一个泛型类型的对象,并赋值为数组里,索引为head的元素。然后head数+1。
3、该方法不会使数组的元素总个数减少,只是删除刚刚获取的数据,将该数据值改为泛型的默认值。然后将指针向后移。这也就给了一个感觉:Queue是先进先出,Dequeue()将获取的元素删除。
[__DynamicallyInvokable]
public T Dequeue()
{
//判断当前集合中是否有元素
if (_size == 0)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue);
}
//创建一个泛型类型的对象,并赋值为数组里的索引为head的元素
T result = _array[_head];
//然后将数组里的索引为head的元素,改为泛型类型的默认值。
_array[_head] = default(T);
_head = (_head + 1) % _array.Length;
_size--;
_version++;
return result;
}