1 队列简介
队列(Queue)
是运算受限的线性表。一种先进先出(First In First Out ,简称FIFO)的线性表。只允许在表的一端进行插入,而在另一端进行删除。先进先出 是队列特有的性质。
举例来说
- 我们购买东西需要排队,而这个排好的队伍就是
队列。先来的人可以先买到东西离开。 - 乘坐电动扶梯时,乘客都是从扶梯的一头进入,另一头离开电梯。先上电梯的乘客,先下电梯。
2 逻辑与实现
2.1 队列的基本功能
理解完队列概念,再来列出队列具有的功能:
插入
向队列 队尾 增加一个对象。
弹出
从队列 队首 弹出一个对象。
计数
计算当前队列中数据的总个数。
2.2 队列的需要的变量
好了,功能也有了,现在来更具这些功能来准备准备我们的材料:
单位容量
FCapacity总要告诉内存你每次打算征用人家多少区域来给你排队用吧。
一块内存
FMem更具你的单位容量,去申请‘场地’来排队用,用完了再来申请。
队尾计数器
FPopIndex有了它,你就知道你在队伍里排的多少号了。
队首计数器
FPushIndex有了它,你就只当队伍最前面的人拍的是几号了。
2.3 队列实现
2.3.1 初始化
先来初始化队列,设置容量并重置计数器。
123456789
PageCapacity = 7;//初始化 计数器FPushIndex = 0;FPopIndex = 0;//更具容量申请 固定大小的空间SetLength(FMem, PageCapacity);FCapacity = PageCapacity;
2.3.2 载入数据
向队列加载数据时,队尾计数器自加,自加后如果发现 排号和容量大小一样了,说明空间不够了,就向商场(内存)申请连续的扩容空间(已用空间大小+单位容量)
12345678910
//新来的元素放到空间末尾FMem[FPushIndex] = NewItem;//每加载一个新的元素进来,都要增加FPushIndexFPushIndex = FPushIndex + 1;//这里还需要保证 队尾 计数器始终小于 总容量 ,否则 要扩容if (FPushIndex >= FCapacity)SetLength(FMem, FCapacity+ PageCapacity);FCapacity = FCapacity + PageCapacity;
2.3.3 弹出数据
好了,现在队伍有了,该卖东西了,正真的队伍是卖一个走一个,所以我们按照号码(内存下标)来叫号卖。
1234
//按照 队首 下标开始 获取元素( FPopIndex 前提小于 排队人数 FPushIndex )if FPopIndex < FPushIndexPopItem = FMem[FPopIndex];FPopIndex = FPopIndex + 1; //每弹出一个元素,都要增加FPopIndex
弹出数据的过程中也会有新的数据来加载,但是不影响我们改变内存的规律:
载入数据- 直接将数据放到队列末尾
- 队尾计数器 +1
- 判断内存是否足够
弹出数据- 拿出队首计数器指向数据
- 队首计数器 +1
2.4 代码实现
废话说了这么多,好了贴代码。
代码功能说明:
- 基本功能
- 载入功能
- 弹出功能
- 清空队列功能
- 队列计数功能
- 自动扩容功能
- 拓展功能
- 为了线程安全,使用临界区加锁控制
- 字符串加载/弹出功能
- 容量设置功能
代码如下:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
unit FastDemo;interface//线程安全版本usesWindows, SysUtils;constCONST_BYTE_SIZE = 4;CONST_PAGE_SINGLE = 1024;COSNT_PAGE_SINGLE_SIZE = CONST_PAGE_SINGLE * CONST_BYTE_SIZE;typeTFastQueue = classprivateFCS: TRTLCriticalSection;//FMem: Pointer;FTmpMem: Pointer;//FPushIndex: Integer; //压入队列计数器FPopIndex: Integer; //弹出队列计数器FCapacity: Integer; //队列容量,始终大于 FPushIndex//procedure Lock();procedure UnLock();procedure SetCapacity(const AValue: Integer);//procedure setSynCapacity(const AValue: Integer);function getSynCapacity: Integer;function getSynCurCount: Integer;publicconstructor Create(AInitCapacity: Integer = CONST_PAGE_SINGLE);destructor Destroy(); override;//function Push(AItem: Pointer): Pointer;function Pop(): Pointer;function PushString(AItem: string): Pointer;function PopString(): string;procedure Clear;publicproperty Capacity: Integer read getSynCapacity write setSynCapacity;property Count: Integer read getSynCurCount;end;implementation{ TFastQueue }constructor TFastQueue.Create(AInitCapacity: Integer);beginInitializeCriticalSection(FCS);FPushIndex := 0;FPopIndex := 0;if AInitCapacity < CONST_PAGE_SINGLE thenAInitCapacity := CONST_PAGE_SINGLE;SetCapacity(AInitCapacity);end;destructor TFastQueue.Destroy;beginFreeMem(FMem);if FTmpMem <> nil thenFreeMem(FTmpMem);DeleteCriticalSection(FCS);inherited;end;procedure TFastQueue.Lock;beginEnterCriticalSection(FCS);end;procedure TFastQueue.UnLock;beginLeaveCriticalSection(FCS);end;procedure TFastQueue.SetCapacity(const AValue: Integer);varvPageCount, vOldSize, vNewSize: Integer;beginif AValue > FCapacity thenbeginif FTmpMem <> nil thenFreeMem(FTmpMem);vPageCount := AValue div CONST_PAGE_SINGLE;if (AValue mod CONST_PAGE_SINGLE) > 0 thenInc(vPageCount);//保存旧的容量vOldSize := FCapacity * CONST_BYTE_SIZE;//计算新的容量FCapacity := vPageCount * CONST_PAGE_SINGLE;vNewSize := FCapacity * CONST_BYTE_SIZE;//扩容GetMem(FTmpMem, vNewSize);FillChar(FTmpMem^, vNewSize, #0);//转移数据if FMem <> nil thenbeginMove(FMem^, FTmpMem^, vOldSize);FreeMem(FMem);end;FMem := FTmpMem;//FTmpMem (保证弹出、插入数据时使用) 与 FMem 大小一致GetMem(FTmpMem, vNewSize);end;end;function TFastQueue.Push(AItem: Pointer): Pointer;varvPMem: PInteger;vNextPriority, vPriority, vIndex: Integer;vNotPushed: boolean;beginLock();tryvPMem := PInteger(FMem);Inc(vPMem, FPushIndex);vPMem^ := Integer(AItem);Inc(FPushIndex);//检测栈容量是否足够(至少保留一位空位,否则扩容 1024)if FPushIndex >= FCapacity thenbeginSetCapacity(FCapacity + CONST_PAGE_SINGLE);end;finallyUnLock();end;end;function TFastQueue.Pop: Pointer;procedure MoveMem();varvvPSrc: PInteger;vvTmpMem: Pointer;beginFillChar(FTmpMem^, FCapacity * CONST_BYTE_SIZE, #0);vvPSrc := PInteger(FMem);Inc(vvPSrc, FPopIndex);Move(vvPSrc^, FTmpMem^, (FCapacity - FPopIndex) * CONST_BYTE_SIZE);//交换指针vvTmpMem := FMem;FMem := FTmpMem;FTmpMem := vvTmpMem;end;varvPMem: PInteger;beginLock();tryResult := nil;if (FPopIndex = FPushIndex) thenExit;// 1.获取弹出元素vPMem := PInteger(FMem);Inc(vPMem, FPopIndex);Result := Pointer(vPMem^);// 2.弹出元素后 弹出计数器 +1Inc(FPopIndex);// 3.队列底部空余内存达到 1024 整体迁移if FPopIndex = CONST_PAGE_SINGLE thenbegin//迁移数据MoveMem();//重置弹出位置FPopIndex := 0;//减少压入队列的数量Dec(FPushIndex, CONST_PAGE_SINGLE);end;finallyUnLock();end;end;function TFastQueue.PushString(AItem: string): Pointer;varvPChar: PChar;beginvPChar := StrAlloc(256);StrCopy(vPChar, pchar(AItem + ' |' + inttostr(FPushIndex)));Push(vPChar);end;function TFastQueue.PopString: string;varvPChar: PChar;beginResult := 'nil';vPChar := Pop;if vPChar <> '' thenbeginResult := vPChar;StrDispose(vPChar);end;end;procedure TFastQueue.Clear;beginLock();tryFPushIndex := 0;FPopIndex := 0;SetCapacity(CONST_PAGE_SINGLE);finallyUnLock();end;end;procedure TFastQueue.setSynCapacity(const AValue: Integer);beginLock();trySetCapacity(AValue);finallyUnLock();end;end;function TFastQueue.getSynCapacity: Integer;beginLock();tryResult := FCapacity;finallyUnLock();end;end;function TFastQueue.getSynCurCount: Integer;beginLock();tryResult := FPushIndex - FPopIndex;finallyUnLock();end;end;end.
2.5 使用Demo
12345678910111213141516171819202122
procedure UseFastQueue();varvQueue: TFastQueue;beginvQueue := TFastQueue.Create;tryvQueue.Push('A1');vQueue.Push('A2');vQueue.Push('A3');vQueue.Push('A4');vQueue.Push('A5');vQueue.Push('A6');while vQueue.Count > 0 dobeginOutputDebugString(vQueue.PopString);end;finallyvQueue.Free;vQueue := nil;end;end;
输出:
A1
A2
A3
A4
A5

