0Pricing
Java Academy · درس

البنية الداخلية لـ LinkedList

استكشف بنية العقد مزدوجة الارتباط في LinkedList وخصائص تعقيدها الزمني

البنية الداخلية لـ LinkedList درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

التركيب الداخلي لـ LinkedList

إن LinkedList في Java عبارة عن قائمة مترابطة مزدوجة: تحتوي كل عقدة على مرجع إلى العقدة السابقة والتالية، بالإضافة إلى قيمة العنصر. وعلى خلاف ArrayList، لا توجد مصفوفة داعمة؛ إذ تُخصَّص الذاكرة لكل عقدة.

class Node<T> {
    T data;
    Node<T> prev;
    Node<T> next;
    Node(T data) { this.data = data; }
}

خصائص التعقيد الزمني

تختلف خصائص أداء LinkedList كثيرًا عن ArrayList:

  • addFirst / addLast: ‏O(1)
  • get(index): ‏O(n) — يجب اجتياز العناصر بدءًا من الرأس أو الذيل
  • remove(index): ‏O(n) للعثور على العنصر، ثم O(1) لفصله
  • Iterator traversal: ‏O(n)

استخدم LinkedList عندما تحتاج إلى عمليات إدراج متكررة في الرأس أو الذيل، وليس إلى الوصول العشوائي.

إنشاء LinkedList واجتيازها

يتبع إنشاء LinkedList والتكرار عليها واجهة List نفسها التي تعرفها بالفعل. ويكمن الاختلاف في البنية الداخلية.

import java.util.LinkedList;

LinkedList<String> list = new LinkedList<>();
list.add("Alice");
list.add("Bob");
list.add("Carol");

for (String name : list) {
    System.out.println(name);
}

System.out.println("First: " + list.getFirst()); // Alice
System.out.println("Last: "  + list.getLast());  // Carol

addFirst وaddLast وremoveFirst وremoveLast

توفّر LinkedList عمليات على الرأس والذيل لا يوفّرها ArrayList بكفاءة:

LinkedList<Integer> nums = new LinkedList<>();
nums.addLast(10);   // [10]
nums.addLast(20);   // [10, 20]
nums.addFirst(5);   // [5, 10, 20]

System.out.println(nums.removeFirst()); // 5  → [10, 20]
System.out.println(nums.removeLast());  // 20 → [10]

فصل العقدة: حذف O(1) بعد العثور عليها

بمجرد امتلاكك مرجعًا إلى عقدة (عبر iterator)، تصبح الإزالة بتعقيد O(1)، إذ لا يلزم سوى تحديث مؤشري next وprev، بخلاف ArrayList الذي يتطلب إزاحة العناصر.

import java.util.*;

LinkedList<String> tasks = new LinkedList<>(List.of("A","B","C","D"));
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String t = it.next();
    if (t.equals("B") || t.equals("D")) {
        it.remove(); // O(1) unlink
    }
}
System.out.println(tasks); // [A, C]

استهلاك الذاكرة مقارنةً بـ ArrayList

تحمل كل عقدة في LinkedList مرجعين إضافيين (prev وnext) بالإضافة إلى مرجع العنصر، أي نحو 48 بايتًا لكل إدخال على JVM ‏64-bit. أما ArrayList فتخزن مرجع العنصر فقط (8 بايتات) في مصفوفة متجاورة.

بالنسبة إلى مجموعات البيانات الكبيرة التي تكثر فيها عمليات القراءة، تكون ArrayList عادةً أفضل من حيث الاستفادة من ذاكرة التخزين المؤقت وتستهلك ذاكرة أقل.

عمليات Deque: المكدس والطابور

تطبّق LinkedList الواجهة Deque، مما يتيح استخدامها كمكدس وكطابور.

import java.util.LinkedList;
import java.util.Deque;

// As a Queue (FIFO)
Deque<String> queue = new LinkedList<>();
queue.offer("first");
queue.offer("second");
System.out.println(queue.poll()); // first

// As a Stack (LIFO)
Deque<String> stack = new LinkedList<>();
stack.push("bottom");
stack.push("top");
System.out.println(stack.pop()); // top

نظرة عامة على PriorityQueue

إن PriorityQueue طابور قائم على heap، حيث يُزال العنصر الأصغر دائمًا أولًا وفق الترتيب الطبيعي أو Comparator. وهي لا تعتمد على قائمة مترابطة، بل تستخدم مصفوفة تمثل heap ثنائيًا.

import java.util.PriorityQueue;

PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(40);
pq.offer(10);
pq.offer(25);

System.out.println(pq.poll()); // 10 (smallest)
System.out.println(pq.poll()); // 25
System.out.println(pq.poll()); // 40

PriorityQueue باستخدام Comparator مخصص

مرّر Comparator لعكس الترتيب أو للفرز حسب حقل مخصص:

import java.util.*;

record Task(String name, int priority) {}

PriorityQueue<Task> tasks = new PriorityQueue<>(
    Comparator.comparingInt(Task::priority).reversed() // highest first
);
tasks.offer(new Task("Low", 1));
tasks.offer(new Task("High", 10));
tasks.offer(new Task("Med", 5));

while (!tasks.isEmpty()) {
    System.out.println(tasks.poll().name());
}
// High, Med, Low

اختيار LinkedList أو ArrayList

قاعدة عامة:

  • استخدم ArrayList للوصول العشوائي والتكرار ومعظم السيناريوهات.
  • استخدم LinkedList عندما تحتاج إلى عمليات إدراج وإزالة متكررة بتعقيد O(1) عند الطرفين، ولا تحتاج إلى الوصول باستخدام الفهرس.
  • استخدم PriorityQueue عندما تحتاج إلى معالجة مرتبة، مثل جدولة المهام أو خوارزمية Dijkstra.

الأخطاء الشائعة

تجنب استدعاء get(i) داخل حلقة على LinkedList، إذ يصبح التعقيد الإجمالي O(n²):

LinkedList<Integer> list = new LinkedList<>();
for (int i = 0; i < 10000; i++) list.add(i);

// BAD: O(n^2) — each get(i) traverses from head
for (int i = 0; i < list.size(); i++) {
    int val = list.get(i); // slow!
}

// GOOD: O(n) — use iterator
for (int val : list) {
    // process val
}

اختبار سريع

ما عملية LinkedList التي يكون تعقيدها O(1) بغض النظر عن حجم القائمة؟

خلاصة: LinkedList وDeque

أهم النقاط:

  • LinkedList قائمة مترابطة مزدوجة، وتنفذ العمليات على الرأس والذيل بتعقيد O(1)
  • الوصول العشوائي (get/set باستخدام الفهرس) تعقيده O(n)
  • تطبّق Deque، ويمكن استخدامها كمكدس أو طابور
  • توفّر PriorityQueue معالجة مرتبة حسب heap
  • فضّل ArrayList في معظم حالات الاستخدام؛ إذ تتفوق LinkedList عند إجراء تغييرات متكررة على الرأس والذيل

الأسئلة الشائعة

هل درس «البنية الداخلية لـ LinkedList» مجاني؟

نعم — نص درس «البنية الداخلية لـ LinkedList» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «البنية الداخلية لـ LinkedList»؟

استكشف بنية العقد مزدوجة الارتباط في LinkedList وخصائص تعقيدها الزمني تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «البنية الداخلية لـ LinkedList»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. البنية الداخلية لـ LinkedList
  2. عمليات Deque: المكدس والطابور
  3. المفاضلة بين LinkedList وArrayList
  4. PriorityQueue للمعالجة المرتبة
← العودة إلى Java Academy