Rust: مجموعات Rust الشائعة: VecDeque وBTreeMap ومقارنة الأداء

آخر تحديث: 2026-08-26

VecDeque هي قائمة انتظار ذات طرفين تدعم عمليات الإدراج والحذف بكفاءة من كلا الطرفين؛ أما BTreeMap فهي خريطة مرتبة للمفاتيح والقيم تدعم استعلامات النطاق. وهما عنصران «متخصصان» في مكتبة المجموعات بلغة Rust.

إذا كان Vec يشبه «الانتظار في طابور لركوب الحافلة» (لا يمكنك الانضمام إلا في نهاية الطابور)، فإن VecDeque يشبه «شخصًا يتخطى الطابور» (يمكنك الدخول والخروج من أي طرف). إذا كانت HashMap تشبه «القاموس» (عمليات بحث عشوائية)، فإن BTreeMap تشبه «قائمة جهات الاتصال» (مرتبة أبجديًا، ويمكنك الانتقال إلى صفحة معينة لبدء القراءة).


1. ما ستتعلمه



2. الرسوم التخطيطية المفاهيمية

يوضح الرسم البياني المقارن التالي الخاص بـ Mermaid الاختلافات بين بنية البيانات «VecDeque» (قائمة الانتظار ذات الطرفين) وبنية البيانات «BTreeMap» (خريطة شجرة B):

100%
graph LR
    subgraph VecDeque["VecDeque Double-ended queue"]
        direction LR
        V1["pop_front O(1)"] --> V2["Element1"]
        V2 --> V3["Element2"]
        V3 --> V4["..."]
        V4 --> V5["ElementN"]
        V5 --> V6["push_back O(1)"]
        V7["push_front O(1)"] -.-> V2
    end

    subgraph BTreeMap["BTreeMap BOrdered Map of Trees"]
        direction TB
        B0["Root Node"] --> B1["Left Branch<br/>Keys: A-M"]
        B0 --> B2["Right Branch<br/>Keys: N-Z"]
        B1 --> B3["(Alice, 95)"]
        B1 --> B4["(Bob, 72)"]
        B2 --> B5["(Charlie, 87)"]
        B2 --> B6["(David, 91)"]
    end

    VecDeque -.-> |Comparison of Data Structures| BTreeMap


3. قصة نظام الطوابير والتذاكر

(1) المشكلة: محاكاة طابور باستخدام Vec، لكن كل من «التخطي في الطابور» و«مغادرة الطابور» عملية بطيئة

يعمل توم (توم) على تطوير نظام لإدارة قوائم الانتظار للمطاعم. يحصل العملاء على رقم عند وصولهم، ثم ينادي النادل الرقم ليقوم بتوجيههم إلى طاولاتهم.

في البداية، قام بتنفيذها باستخدام Vec:

RUST
let mut queue = Vec::new();
queue.push("Customer A");   // A Get a ticket,At the back of the line
queue.push("Customer B");   // B Get a ticket,At the back of the line
queue.push("Customer C");   // C Get a ticket,At the back of the line

// Take a seat when your number is called:Remove from the front of the line
let first = queue.remove(0);  // O(n) —— All elements that follow must be moved forward!
println!("{} Take a seat", first);

عملية Vec remove(0) هي عملية من الدرجة O(n) — بعد إزالة العنصر الأول، يجب إزاحة جميع العناصر المتبقية إلى الأمام بمقدار موضع واحد. إذا كان هناك 10,000 شخص في الطابور، ففي كل مرة يتم فيها نداء رقم ما، يجب إزاحة 9,999 عنصرًا. ومن الواضح أن هذا غير مناسب لنظام إنتاجي.

(2) متطلبات أكثر تعقيدًا: أولوية كبار الشخصيات + البحث حسب الرقم

ومما زاد الطين بلة، قال مدير المطعم:

لا تستطيع «Vec» تلبية هذه المتطلبات على الإطلاق.

(3) حلول لمجموعات «Rust»

RUST
use std::collections::VecDeque;
use std::collections::BTreeMap;

fn main() {
    // VecDeque:Accessible from both the front and the back
    let mut queue = VecDeque::new();
    queue.push_back("Customer A");   // Enter at the back of the line
    queue.push_back("Customer B");
    queue.push_front("VIP");         // VIP Cutting in line at the front of the line!

    println!("Next, please take a seat.: {}", queue.pop_front().unwrap());  // VIP
    println!("Next: {}", queue.pop_front().unwrap());    // Customer A

    // BTreeMap:Ordered Key-Value Store
    let mut customers = BTreeMap::new();
    customers.insert(1001, "Alice");
    customers.insert(1003, "Bob");
    customers.insert(1002, "Charlie");

    // Automatically Sort and Output Buttons
    for (id, name) in &customers {
        println!("Get a ticket #{}: {}", id, name);
    }
    // Output: #1001: Alice, #1002: Charlie, #1003: Bob
}

العمليات التي تُجرى على طرفي VecDeque هي من الدرجة O(1)، في حين أن BTreeMap تقوم تلقائيًا بفرز المفاتيح وتدعم استعلامات النطاق. فالأول يحل مشكلة «الانتظار في الطابور»، والثاني يحل مشكلة «البحث المرتب».



4. المفاهيم الأساسية

(1) مقارنة بين أربع هياكل للمجموعات

100%
graph TB
    A[Rust Collection Library] --> B[Vec: Contiguous Arrays]
    A --> C[VecDeque: Double-ended queue]
    A --> D[HashMap: Hash Table]
    A --> E[BTreeMap: B Ordered Map of Trees]

    B --> F["push / pop: O(1) Amortized"]
    B --> G["insert(0) / remove(0): O(n)"]

    C --> H["push_front / pop_front: O(1)"]
    C --> I["push_back / pop_back: O(1)"]

    D --> J["Insert / Search: O(1) Amortized"]
    D --> K["Disorder —— No order guarantee"]

    E --> L["Insert / Search: O(log n)"]
    E --> M["Orderly —— Support for Range Queries range"]

(2) مقارنة أداء أربعة أنواع من المجموعات

العملية Vec VecDeque HashMap BTreeMap
إدراج الذيل O(1) (متوسط) O(1) O(1) (متوسط) O(log n)
الإدراج في مقدمة القائمة O(n) O(1)
الوصول حسب الفهرس O(1) O(1)
البحث عن المفتاح O(1) (متوسط) O(log n)
استعلام النطاق غير مدعوم غير مدعوم غير مدعوم O(log n + k)
التصفح المرتب يجب أن تكون مرتبة يجب أن تكون مرتبة غير مرتبة O(n) (مرتبة)
استخدام الذاكرة منخفض منخفض متوسط متوسط

(3) استراتيجية الاختيار

السيناريو المجموعة الموصى بها السبب
قائمة الانتظار / المخزن المؤقت (العمليات على كلا الطرفين) VecDeque O(1) على كلا الطرفين — لا يوجد ما هو أنسب من هذا
يتطلب مسحًا مرتبًا حسب المفتاح BTreeMap مرتبة تلقائيًا؛ لا تتطلب خطوات فرز إضافية
استعلامات النطاق المطلوبة BTreeMap المجموعة الوحيدة التي تدعم استعلامات النطاق
لا تتطلب سوى عمليات «tail» Vec أبسط وأكثر كفاءة في استخدام الذاكرة
لا يتطلب سوى عمليات البحث عن القيم باستخدام المفاتيح HashMap عمليات بحث بزمن O(1)، أسرع من BTreeMap
الإدراج والحذف المتكرر للعناصر الوسطى LinkedList O(1) نظريًّا، لكن نادرًا ما تُستخدم في الممارسة العملية

(4) مرجع سريع للطرق الشائعة في VecDeque و BTreeMap

المجموعة الطريقة الوصف التعقيد الزمني
VecDeque push_front(val) الإدراج في المقدمة O(1)
VecDeque push_back(val) الإدراج في النهاية O(1)
VecDeque pop_front() استخراج العنصر الأول من المصفوفة O(1)
VecDeque pop_back() سحب عنصر من النهاية O(1)
VecDeque front() / back() عرض العنصر الأول والأخير O(1)
VecDeque len() / is_empty() استعلام الطول O(1)
BTreeMap insert(k, v) إدراج زوج المفتاح والقيمة O(log n)
BTreeMap get(&k) البحث باستخدام المفتاح O(log n)
BTreeMap remove(&k) الحذف حسب المفتاح O(log n)
BTreeMap range(start..=end) استعلام النطاق O(log n + k)
BTreeMap first_key_value() أقل عدد من أزواج المفتاح-القيمة O(log n)
BTreeMap last_key_value() الحد الأقصى لزوج المفتاح-القيمة O(log n)


5. تقديم الأمثلة

(1) ▶ المثال:قائمة انتظار ثنائية الأطراف باستخدام VecDeque — نظام الانتظار وإصدار التذاكر (مستوى الصعوبة ⭐)

RUST
// ============================================
// VecDeque:Basic Operations on a Double-Ended Queue
// ============================================

use std::collections::VecDeque;

fn main() {
    let mut queue: VecDeque<&str> = VecDeque::new();

    // Regular customers enter from the back of the line
    queue.push_back("Alice");
    queue.push_back("Bob");
    queue.push_back("Charlie");

    // VIP A customer cut in line from the front of the line
    queue.push_front("VIP-David");
    queue.push_front("VIP-Eve");

    println!("Current number of people in line: {}", queue.len());
    println!("At the head of the line: {:?}, Bringing up the rear: {:?}", queue.front(), queue.back());

    // Take a seat when your number is called:Remove from the front of the queue
    println!("\nCall Order:");
    while let Some(customer) = queue.pop_front() {
        println!("  {} Take a seat", customer);
    }
}

الناتج:

TEXT 📖 للعرض فقط
Current number of people in line: 5
At the head of the line: Some("VIP-Eve"), Bringing up the rear: Some("Charlie")

Call Order:
  VIP-Eve Take a seat
  VIP-David Take a seat
  Alice Take a seat
  Bob Take a seat
  Charlie Take a seat

تعد كل من push_front() وpush_back() عمليتين من الدرجة O(1). وتُرجع كل من front() وback() مؤشرات إلى مقدمة وقاع قائمة الانتظار، على التوالي، دون إزالة العناصر. أما pop_front() فتزيل العنصر الموجود في مقدمة قائمة الانتظار وتُرجع رقم ترتيبه.


(2) ▶ المثال:BTreeMap — تخزين القيم والمفاتيح مرتبةً: تصنيفات الدرجات (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// BTreeMap:Automatic Button Sorting
// ============================================

use std::collections::BTreeMap;

fn main() {
    let mut scores = BTreeMap::new();

    scores.insert("Charlie", 87);
    scores.insert("Alice", 95);
    scores.insert("Bob", 72);
    scores.insert("David", 91);

    // BTreeMap Button(Alphabetical order)Automatic Sorting
    println!("=== Transcript(Sort by Name)===");
    for (name, score) in &scores {
        println!("  {}: {}", name, score);
    }

    // Search by Key
    println!("\nAlice the results: {}", scores.get("Alice").unwrap());

    // Check for the presence of
    if scores.contains_key("Eve") {
        println!("Eve There are discrepancies in the scores");
    } else {
        println!("Eve No results yet");
    }

    // Get the first and last entries
    println!("\nFirst: {:?}", scores.first_key_value());
    println!("The last one: {:?}", scores.last_key_value());
}

الناتج:

TEXT 📖 للعرض فقط
=== Transcript(Sort by Name)===
  Alice: 95
  Bob: 72
  Charlie: 87
  David: 91

Alice the results: 95
Eve No results yet

First: Some(("Alice", 95))
The last one: Some(("David", 91))

تقوم BTreeMap تلقائيًا بفرز المفاتيح حسب ترتيب Ord. ترتيب الفرز الافتراضي للسلاسل هو الترتيب المعجمي، لذا تأتي كلمة "Alice" أولاً وتأتي كلمة "David" أخيرًا. توفر first_key_value() وlast_key_value() وصولاً فعالاً إلى العنصر الأول والعنصر الأخير.


(3) ▶ المثال:استعلام النطاق في BTreeMap — استعلام النطاق الزمني (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// BTreeMap Range Query:range Methods
// ============================================

use std::collections::BTreeMap;

fn main() {
    // Simulated Order System:Date -> Order Amount
    let mut orders = BTreeMap::new();

    orders.insert("2026-07-01", 120);
    orders.insert("2026-07-03", 85);
    orders.insert("2026-07-05", 200);
    orders.insert("2026-07-07", 150);
    orders.insert("2026-07-10", 95);

    // Search orders placed between July 3 and July 7 (inclusive)
    println!("=== July 3 ~ July 7 Orders ===");
    for (date, amount) in orders.range("2026-07-03"..="2026-07-07") {
        println!("  {}: ${}", date, amount);
    }

    // Search all orders on or after July 5
    println!("\n=== July 5 and After Orders ===");
    for (date, amount) in orders.range("2026-07-05"..) {
        println!("  {}: ${}", date, amount);
    }

    // Total Amount
    let total: i32 = orders.range("2026-07-03"..="2026-07-07")
        .map(|(_, amount)| amount)
        .sum();
    println!("\nJuly 3 ~ July 7 Total Amount: ${}", total);
}

الناتج:

TEXT 📖 للعرض فقط
=== July 3 ~ July 7 Orders ===
  2026-07-03: $85
  2026-07-05: $200
  2026-07-07: $150

=== July 5 and After Orders ===
  2026-07-05: $200
  2026-07-07: $150
  2026-07-10: $95

July 3 ~ July 7 Total Amount: $435

range() هي طريقة خاصة بـ BTreeMap. وهي تقبل تعبير نطاق (.. للفاصل المفتوح، و..= للفاصل المغلق، وstart.. للفاصل نصف المفتوح) وتُرجع مُكررًا. وهذا أمر لا تستطيع HashMap القيام به.


(4) ▶ المثال:مقارنة أداء أربعة أنواع من المجموعات (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// Comparison of the Performance of Four Types of Sets:Insert 10000 element
// ============================================

use std::collections::{BTreeMap, HashMap, VecDeque};
use std::time::Instant;

fn main() {
    let n = 10_000;

    // Vec Insert at the end
    let start = Instant::now();
    let mut vec = Vec::new();
    for i in 0..n {
        vec.push(i);
    }
    println!("Vec Insert at the end: {:?}", start.elapsed());

    // VecDeque Insert at the end
    let start = Instant::now();
    let mut deque = VecDeque::new();
    for i in 0..n {
        deque.push_back(i);
    }
    println!("VecDeque Insert at the end: {:?}", start.elapsed());

    // HashMap Insert
    let start = Instant::now();
    let mut hmap = HashMap::new();
    for i in 0..n {
        hmap.insert(i, i);
    }
    println!("HashMap Insert: {:?}", start.elapsed());

    // BTreeMap Insert
    let start = Instant::now();
    let mut bmap = BTreeMap::new();
    for i in 0..n {
        bmap.insert(i, i);
    }
    println!("BTreeMap Insert: {:?}", start.elapsed());

    // Search for Performance Comparisons
    println!("\n--- Search Performance ---");

    let start = Instant::now();
    let _ = vec.contains(&9999);
    println!("Vec Linear Search: {:?}", start.elapsed());

    let start = Instant::now();
    let _ = hmap.get(&9999);
    println!("HashMap Search: {:?}", start.elapsed());

    let start = Instant::now();
    let _ = bmap.get(&9999);
    println!("BTreeMap Search: {:?}", start.elapsed());
}

النتيجة (مثال؛ قد يختلف الوقت الفعلي حسب الجهاز):

TEXT 📖 للعرض فقط
Vec Insert at the end: 78.2µs
VecDeque Insert at the end: 82.1µs
HashMap Insert: 1.2ms
BTreeMap Insert: 2.8ms

--- Search Performance ---
Vec Linear Search: 42.5µs
HashMap Search: 138ns
BTreeMap Search: 312ns

تُظهر بيانات الأداء أن HashMap هي الأسرع في عمليات البحث (O(1))، تليها BTreeMap (O(log n))، في حين أن Vec هي الأبطأ في عمليات البحث الخطي (O(n)). أما بالنسبة لعمليات الإدراج، فإن الإدراج في نهاية القائمة هو الأسرع مع Vec وVecDeque، والأبطأ مع BTreeMap. وهذا يؤكد أنه «لا توجد حلول سحرية» — فاختيار المجموعة يعتمد على حالة الاستخدام المحددة.


(5) ▶ المثال:تمرين شامل — نظام جدولة المهام (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example:VecDeque Queue + BTreeMap Priority
// ============================================

use std::collections::{VecDeque, BTreeMap};

#[derive(Debug, Clone)]
struct Task {
    id: u32,
    name: String,
    priority: u8,
}

fn main() {
    let mut queue: VecDeque<Task> = VecDeque::new();
    queue.push_back(Task { id: 1, name: "Check email".into(), priority: 3 });
    queue.push_back(Task { id: 2, name: "Fix bug #42".into(), priority: 1 });
    queue.push_back(Task { id: 3, name: "Write report".into(), priority: 2 });
    queue.push_back(Task { id: 4, name: "Urgent deploy".into(), priority: 0 });

    println!("=== FIFO Queue Processing ===");
    while let Some(task) = queue.pop_front() {
        println!("[P{}] #{}: {}", task.priority, task.id, task.name);
    }

    let mut priority_map: BTreeMap<u8, Vec<String>> = BTreeMap::new();
    let tasks = vec![
        ("Check email", 3u8), ("Fix bug #42", 1), ("Write report", 2),
        ("Urgent deploy", 0), ("Code review", 1), ("Team meeting", 3),
        ("Security patch", 0), ("Update docs", 2),
    ];

    for (name, pri) in tasks {
        priority_map.entry(pri).or_insert_with(Vec::new).push(name.to_string());
    }

    println!("\n=== Sort by priority ===");
    for (pri, task_list) in &priority_map {
        println!("P{}: {:?}", pri, task_list);
    }

    println!("\n=== High-Priority Tasks (P0-P1) ===");
    for (pri, task_list) in priority_map.range(0..=1) {
        println!("P{}: {:?}", pri, task_list);
    }

    let mut history: VecDeque<String> = VecDeque::with_capacity(3);
    for i in 0..5 {
        history.push_back(format!("task_{}", i));
        if history.len() > 3 {
            history.pop_front();
        }
    }
    println!("\nLast 3 history entries: {:?}", history);
}

الناتج:

TEXT 📖 للعرض فقط
=== FIFO Queue Processing ===
[P3] #1: Check email
[P1] #2: Fix bug #42
[P2] #3: Write report
[P0] #4: Urgent deploy

=== Sort by priority ===
P0: ["Urgent deploy", "Security patch"]
P1: ["Fix bug #42", "Code review"]
P2: ["Write report", "Update docs"]
P3: ["Check email", "Team meeting"]

=== High-Priority Tasks (P0-P1) ===
P0: ["Urgent deploy", "Security patch"]
P1: ["Fix bug #42", "Code review"]

Last 3 history entries: ["task_2", "task_3", "task_4"]

استخدم VecDeque كقائمة انتظار من نوع FIFO (push_back + pop_front)؛ حيث تقوم BTreeMap بتجميع العناصر حسب الأولوية وفرزها تلقائيًا؛ بينما يعرض range(0..=1) المهام ذات الأولوية العالية فقط؛ ويُنفذ VecDeque ذو السعة الثابتة سجلًا لـ «آخر N إدخالات».



❓ أسئلة شائعة

س ما الفرق بين VecDeque و Vec؟ متى يجب استخدام VecDeque؟
ج يدعم VecDeque العمليات ذات الوقت O(1) في كلا الطرفين، بينما يدعم Vec العمليات ذات الوقت O(1) في الذيل فقط.
س ما الفرق بين BTreeMap و HashMap؟
ج BTreeMap مرتبة، بينما HashMap غير مرتبة.
س لماذا نادرًا ما يُستخدم LinkedList في لغة Rust؟
ج كل عقدة في LinkedList تتسبب في عبء إضافي على المؤشرات، كما أنها غير ملائمة للذاكرة المؤقتة.
س كيف تنفذ BTreeMap عملية تقسيم الصفحات لعملية range؟
ج تستخدم next() وnth() لتنفيذ تقسيم الصفحات القائم على المؤشر.
س هل هناك طريقة بسيطة لتذكر كيفية الاختيار بين أنواع المجموعات الأربعة؟
ج نعم. «استخدم Vec للعمليات التي تتم في نهاية المجموعة، وVecDeque للعمليات التي تتم في كلا الطرفين، وHashMap للبحث غير المرتب، وBTreeMap للبحث المرتب.»

📖 ملخص


📝 تمارين

  1. الصعوبة ⭐: قم بتنفيذ «سجل التراجع» باستخدام VecDeque. سجل كل عملية باستخدام push_back، وعند التراجع، احذف العملية الأحدث باستخدام pop_back. قم بمحاكاة 3 عمليات تليها عمليتا تراجع، واطبع محتوى كل عملية.
  2. الصعوبة ⭐⭐: قم بتنفيذ «نظام إدارة درجات الطلاب» باستخدام خريطة شجرة B. أدخل أسماء ودرجات 5 طلاب، واطبع الترتيب التنازلي حسب الدرجة (تلميح: لا يمكن لخريطة شجرة B الفرز مباشرةً حسب القيمة؛ يجب عليك استخدام الدرجة كمفتاح والاسم كقيمة، أو استخدام iter().rev()).
  3. الصعوبة ⭐⭐⭐: اكتب دالة fn analyze_collections(data: &[i32]) تتلقى شريحة من الأعداد الصحيحة كمدخلات، وتحسب عدد مرات تكرار كل رقم باستخدام VecDeque وHashMap وBTreeMap على التوالي، وتقارن الفروق في الأداء بين الطرق الثلاث (باستخدام std::time::Instant لقياس الوقت).
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%