2024年5月21日 星期二

4.VB.NET 進階篇 筆記 - 佇列(Queue)

VB.NET Queue 佇列 (Queue) 筆記(基礎篇)

VB.NET Queue 佇列 筆記(基礎篇)

有些資料不能隨意挑選處理順序,而是必須按照進來的先後依序處理。例如櫃台叫號、待辦工單、背景任務、訊息排隊,都屬於「先排隊、先處理」的流程。

Queue 就是用來處理這種排隊資料的集合。Queue 的核心規則是 先進先出(FIFO, First In First Out):最早加入的資料,會最先被取出。

佇列原理分析

用排隊窗口理解 Queue

Queue 可視為一條排隊隊伍。新資料只能排到隊伍尾端,處理資料時只能從隊伍前端開始。中間的資料不能被直接抽走,也不能插隊到前面。

  • 加入資料:使用 Enqueue,資料會排到 Queue 尾端。
  • 取出資料:使用 Dequeue,會取出 Queue 前端最早加入的資料。
  • 查看下一筆:使用 Peek,只查看前端資料,不會移除。
  • 檢查數量:使用 Count,可確認目前 Queue 內還有幾筆資料。

Queue 可以先記住四個重點:

  1. Enqueue:加入尾端。
  2. Dequeue:取出並移除前端。
  3. Peek:查看前端但不移除。
  4. Count:確認目前筆數,避免空 Queue 取值錯誤。

基本操作與語法實作

Windows Forms 物件配置

以下範例皆以 Windows Forms 為主。請於 Form1 放置下列控制項:

  • Button1:建立資料或加入新資料。
  • Button2:取出下一筆資料。
  • Button3:查看下一筆資料。
  • TextBox1:輸入任務名稱。
  • ListBox1:顯示目前 Queue 內容。
  • Label1:顯示目前狀態。

場景一:建立 Queue 並依序取出資料

此範例建立一組待處理項目。按下 Button1 建立 Queue,按下 Button2 取出最前面的資料。取出後,該資料會從 Queue 中移除。

範例程式碼
VB.NET / Windows Forms
Imports System.Collections.Generic

Public Class Form1
    Private serviceQueue As New Queue(Of String)

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        serviceQueue.Clear()

        serviceQueue.Enqueue("文件審核")
        serviceQueue.Enqueue("帳號建立")
        serviceQueue.Enqueue("權限配置")

        RefreshQueueDisplay()
        Label1.Text = "已建立待處理佇列,共 " & serviceQueue.Count.ToString() & " 筆"
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If serviceQueue.Count > 0 Then
            Dim currentItem As String = serviceQueue.Dequeue()
            RefreshQueueDisplay()
            Label1.Text = "已取出項目:" & currentItem
        Else
            Label1.Text = "Queue 目前沒有可處理資料"
        End If
    End Sub

    Private Sub RefreshQueueDisplay()
        ListBox1.Items.Clear()

        For Each item As String In serviceQueue
            ListBox1.Items.Add(item)
        Next
    End Sub
End Class
畫面輸出結果(先按 Button1,再按 Button2)
ListBox1: 帳號建立 權限配置 Label1: 已取出項目:文件審核
邏輯解析
  • New Queue(Of String) 建立只能存放文字的佇列。
  • Enqueue 依序加入「文件審核」、「帳號建立」、「權限配置」。
  • Dequeue 會取出最早加入的「文件審核」,並將它從 Queue 移除。
  • RefreshQueueDisplay 只負責重新顯示目前剩下的 Queue 內容。

場景二:使用 Peek 查看下一筆資料

Peek 適合用於「先看下一筆是誰,但暫時不要處理」的情境。例如叫號系統可先顯示下一位編號,但尚未真的叫號完成。

範例程式碼
VB.NET / Windows Forms
Imports System.Collections.Generic

Public Class Form1
    Private numberQueue As New Queue(Of String)

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        numberQueue.Clear()

        numberQueue.Enqueue("B101")
        numberQueue.Enqueue("B102")
        numberQueue.Enqueue("B103")

        RefreshQueueDisplay()
        Label1.Text = "目前排隊總數:" & numberQueue.Count.ToString()
    End Sub

    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        If numberQueue.Count > 0 Then
            Dim nextItem As String = numberQueue.Peek()
            Label1.Text = "下一位待處理編號:" & nextItem & ",目前總數仍為 " & numberQueue.Count.ToString()
        Else
            Label1.Text = "Queue 目前為空"
        End If
    End Sub

    Private Sub RefreshQueueDisplay()
        ListBox1.Items.Clear()

        For Each number As String In numberQueue
            ListBox1.Items.Add(number)
        Next
    End Sub
End Class
畫面輸出結果(先按 Button1,再按 Button3)
ListBox1: B101 B102 B103 Label1: 下一位待處理編號:B101,目前總數仍為 3
邏輯解析
  • Peek 只讀取前端資料,不會移除資料。
  • 執行 Peek 後,Count 仍然是 3。
  • 畫面中的 ListBox1 仍保留 B101、B102、B103。
  • 若需要真正處理並移除資料,應使用 Dequeue

場景三:以 Queue 製作簡易待辦工作佇列

此範例改成由 TextBox1 輸入任務名稱。每次按下 Button1,任務會排到 Queue 尾端;每次按下 Button2,會取出最早加入的任務。

範例程式碼
VB.NET / Windows Forms
Imports System.Collections.Generic

Public Class Form1
    Private taskQueue As New Queue(Of String)

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim taskName As String = TextBox1.Text.Trim()

        If taskName <> "" Then
            taskQueue.Enqueue(taskName)
            TextBox1.Clear()
            RefreshTaskDisplay()
            Label1.Text = "任務已加入佇列,目前共有 " & taskQueue.Count.ToString() & " 筆"
        Else
            Label1.Text = "請先輸入任務名稱"
        End If
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If taskQueue.Count > 0 Then
            Dim currentTask As String = taskQueue.Dequeue()
            RefreshTaskDisplay()
            Label1.Text = "目前執行任務:" & currentTask
        Else
            Label1.Text = "目前沒有待執行任務"
        End If
    End Sub

    Private Sub RefreshTaskDisplay()
        ListBox1.Items.Clear()

        For Each task As String In taskQueue
            ListBox1.Items.Add(task)
        Next
    End Sub
End Class
畫面輸出結果(依序加入「資料匯入、寄送通知、輸出報表」,再按 Button2)
ListBox1: 寄送通知 輸出報表 Label1: 目前執行任務:資料匯入
邏輯解析
  • TextBox1.Text.Trim() 先去除前後空白,避免只有空白的資料被加入。
  • Enqueue(taskName) 會將新任務排到尾端。
  • Dequeue 會取出最早加入的任務,所以「資料匯入」最先被執行。
  • Queue 適合這種任務派送流程,因為任務處理順序必須和加入順序一致。

進一步應用與實務觀念

場景四:以 Queue 模擬資料緩衝區

緩衝區可視為暫存資料的排隊區。資料先進入 Queue,之後再依序取出。此範例限制 Queue 最多只能保留三筆資料,避免資料無限制累積。

範例程式碼
VB.NET / Windows Forms
Imports System.Collections.Generic

Public Class Form1
    Private bufferQueue As New Queue(Of Integer)
    Private bufferLimit As Integer = 3
    Private nextValue As Integer = 100

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If bufferQueue.Count < bufferLimit Then
            bufferQueue.Enqueue(nextValue)
            Label1.Text = "已寫入資料:" & nextValue.ToString()
            nextValue += 1
        Else
            Label1.Text = "緩衝區已滿,暫時無法新增資料"
        End If

        RefreshBufferDisplay()
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If bufferQueue.Count > 0 Then
            Dim currentValue As Integer = bufferQueue.Dequeue()
            Label1.Text = "已取出資料:" & currentValue.ToString()
        Else
            Label1.Text = "緩衝區目前為空"
        End If

        RefreshBufferDisplay()
    End Sub

    Private Sub RefreshBufferDisplay()
        ListBox1.Items.Clear()

        For Each item As Integer In bufferQueue
            ListBox1.Items.Add(item)
        Next
    End Sub
End Class
畫面輸出結果(連續按 Button1 四次)
ListBox1: 100 101 102 Label1: 緩衝區已滿,暫時無法新增資料
邏輯解析
  • bufferLimit 設為 3,代表最多只允許三筆資料停留在 Queue 中。
  • nextValue 用來產生連續資料編號,避免取出資料後再次加入時出現重複編號。
  • Count < bufferLimit 成立時才允許 Enqueue
  • Queue 滿了以後,程式只顯示提醒,不再加入新資料。

Queue 適合與不適合的地方

  • 適合:依加入順序處理資料,例如工單、叫號、背景任務、訊息暫存。
  • 不適合:需要直接找中間某一筆資料,或經常依條件搜尋資料。
  • 重要觀念:EnqueueDequeuePeek 都圍繞「前端與尾端」運作,不是任意位置存取。

使用限制與常見注意事項

空 Queue 不可直接 Dequeue 或 Peek

若 Queue 內沒有任何元素,卻直接呼叫 DequeuePeek,會產生 InvalidOperationException。因此在取值前,應先檢查 Count 是否大於 0。

場景五:安全取出 Queue 資料

此範例示範 Queue 的基本防呆寫法。取出資料前先檢查 Count,確定仍有資料後才執行 Dequeue

範例程式碼
VB.NET / Windows Forms
Imports System.Collections.Generic

Public Class Form1
    Private myQueue As New Queue(Of String)

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        myQueue.Clear()
        myQueue.Enqueue("第一筆資料")
        myQueue.Enqueue("第二筆資料")

        RefreshQueueDisplay()
        Label1.Text = "測試資料已建立"
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If myQueue.Count > 0 Then
            Dim currentItem As String = myQueue.Dequeue()
            RefreshQueueDisplay()
            Label1.Text = "已處理:" & currentItem
        Else
            Label1.Text = "Queue 目前為空,無法取出資料"
        End If
    End Sub

    Private Sub RefreshQueueDisplay()
        ListBox1.Items.Clear()

        For Each item As String In myQueue
            ListBox1.Items.Add(item)
        Next
    End Sub
End Class
畫面輸出結果(資料取完後再次按 Button2)
ListBox1: Label1: Queue 目前為空,無法取出資料
邏輯解析
  • myQueue.Count > 0 先確認 Queue 仍有資料。
  • 條件成立時才執行 Dequeue,可避免空 Queue 取值錯誤。
  • 條件不成立時只顯示提醒,不執行取出動作。
  • 這種判斷應固定保留在 DequeuePeek 前方。

Queue 使用時應注意:

  • 不適合隨機存取:Queue 著重前端取出與尾端加入,不適合直接操作中間元素。
  • 不適合大量搜尋:若需求是依關鍵字快速查找資料,Dictionary 或其他結構通常更合適。
  • 多執行緒需另外處理:若多個執行緒同時操作同一個 Queue,需加入同步控制,或改用並行集合。
  • 資料順序不可任意調整:Queue 的價值在於保留先後順序,若需要排序,應改用其他集合或先轉成其他資料結構。

Queue 與 Stack 的適用情境比較

比較項目 Queue Stack
處理順序 先進先出(FIFO)。 後進先出(LIFO)。
取出位置 從最早加入的資料開始取出。 從最後加入的資料開始取出。
常見用途 叫號、工單派送、背景任務、訊息佇列。 復原操作、瀏覽紀錄回退、巢狀流程、深度優先處理。
適用情境 資料需要按照到達順序處理。 最後加入的資料需要優先處理。

重點整理

  1. Queue 是先進先出集合,最早加入的資料會最先被取出。
  2. Enqueue 用來加入資料,資料會排到 Queue 尾端。
  3. Dequeue 用來取出並移除前端資料。
  4. Peek 用來查看前端資料,但不會移除資料。
  5. DequeuePeek 前應先檢查 Count > 0,避免空 Queue 取值錯誤。
  6. Queue 適合排隊、派工、緩衝與順序處理,不適合隨機存取、排序或大量搜尋。