0Pricing
Java Academy · درس

ListIterator والتنقل ثنائي الاتجاه

استخدم ListIterator للتنقل في القوائم إلى الأمام والخلف وتعديل العناصر أثناء التكرار

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

ListIterator

توسّع ListIterator<T> واجهة Iterator<T> بإتاحة الاجتياز إلى الخلف، وتحديد الموضع بالاعتماد على الفهرس، وإضافة العناصر واستبدالها أثناء التكرار.

واجهة ListIterator البرمجية

أساليب إضافية تتجاوز Iterator: hasPrevious() وprevious() وnextIndex() وpreviousIndex() وset() وadd().

import java.util.*;

List<String> list = new ArrayList<>(List.of("A", "B", "C", "D"));
ListIterator<String> lit = list.listIterator();

// Forward traversal
while (lit.hasNext()) {
    System.out.print(lit.nextIndex() + ":" + lit.next() + " ");
}
// 0:A 1:B 2:C 3:D

System.out.println();

// Backward traversal
while (lit.hasPrevious()) {
    System.out.print(lit.previousIndex() + ":" + lit.previous() + " ");
}
// 3:D 2:C 1:B 0:A

البدء من موضع

أنشئ ListIterator بدءًا من فهرس محدد باستخدام listIterator(index).

List<Integer> nums = new ArrayList<>(List.of(10, 20, 30, 40, 50));

// Start at index 2 (30)
ListIterator<Integer> lit = nums.listIterator(2);

System.out.println(lit.next());     // 30
System.out.println(lit.next());     // 40
System.out.println(lit.previous()); // 40
System.out.println(lit.previous()); // 30
System.out.println(lit.previous()); // 20

استخدام set() أثناء التكرار

يستبدل set() آخر عنصر أرجعته next() أو previous(). وتتم هذه العملية بكفاءة — بتعقيد O(1) في LinkedList وO(1) في ArrayList.

List<String> words = new ArrayList<>(List.of("hello", "world", "java"));
ListIterator<String> lit = words.listIterator();

while (lit.hasNext()) {
    String word = lit.next();
    lit.set(word.toUpperCase()); // replace each with uppercase
}

System.out.println(words); // [HELLO, WORLD, JAVA]

استخدام add() أثناء التكرار

يُدرج add(element) العنصر قبل الموضع التالي. ولا تُرجع عملية استدعاء next() اللاحقة العنصر المُضاف — لأنه أصبح بالفعل «خلف» المؤشر.

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3));
ListIterator<Integer> lit = numbers.listIterator();

while (lit.hasNext()) {
    int n = lit.next();
    lit.add(n * 10); // insert n*10 after each element
}

System.out.println(numbers); // [1, 10, 2, 20, 3, 30]

عكس قائمة في موضعها

استخدام ListIterator لعكس قائمة بتعقيد O(n) دون تخصيص قائمة جديدة.

static <T> void reverse(List<T> list) {
    ListIterator<T> front = list.listIterator(0);
    ListIterator<T> back  = list.listIterator(list.size());

    for (int i = 0, n = list.size() / 2; i < n; i++) {
        T frontVal = front.next();
        T backVal  = back.previous();
        front.set(backVal);
        back.set(frontVal);
    }
}

List<String> data = new ArrayList<>(List.of("a","b","c","d","e"));
reverse(data);
System.out.println(data); // [e, d, c, b, a]

nextIndex وpreviousIndex

تُرجع nextIndex() فهرس العنصر الذي ستُرجعه next(). وتُرجع previousIndex() فهرس العنصر الذي ستُرجعه previous().

List<String> list = List.of("X", "Y", "Z");
ListIterator<String> lit = list.listIterator();

System.out.println(lit.nextIndex());     // 0
System.out.println(lit.previousIndex()); // -1 (before start)

lit.next(); // consume X
System.out.println(lit.nextIndex());     // 1
System.out.println(lit.previousIndex()); // 0

أداء ListIterator في LinkedList

يحقق ListIterator في LinkedList تعقيد O(1) عند استدعاء next/previous، لأن الت traversing يتم باتباع المؤشرات. أما get(i) في LinkedList فتعقيده O(n) — لذا استخدم ListIterator للتنقل بكفاءة.

import java.util.*;

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

// Efficient: O(n) total for traversal via ListIterator
ListIterator<Integer> lit = list.listIterator();
while (lit.hasNext()) {
    int val = lit.next();
    if (val == 20) lit.set(99); // O(1) update
}
System.out.println(list); // [0, 10, 99, 30, 40]

التحقق من Palindrome باستخدام ListIterator

مثال أنيق على التنقل ثنائي الاتجاه: التحقق مما إذا كانت القائمة متناظرة.

static <T> boolean isPalindrome(List<T> list) {
    ListIterator<T> front = list.listIterator(0);
    ListIterator<T> back  = list.listIterator(list.size());

    for (int i = 0, n = list.size() / 2; i < n; i++) {
        if (!front.next().equals(back.previous())) return false;
    }
    return true;
}

System.out.println(isPalindrome(List.of(1, 2, 3, 2, 1))); // true
System.out.println(isPalindrome(List.of(1, 2, 3, 4)));    // false

مقارنة Iterator وListIterator

الفروقات الأساسية:

  • Iterator: اتجاه أمامي فقط، مع hasNext/next/remove
  • ListIterator: ثنائي الاتجاه، مع hasPrevious/previous وnextIndex/previousIndex وset/add
  • يُستخدم ListIterator مع Lists فقط، وليس مع Set أو Queue

التراجع والإعادة باستخدام ListIterator

محرر نصوص قائم على مؤشر يستخدم ListIterator للتنقل الفعال بين الأحرف أثناء التراجع والإعادة.

import java.util.*;

class TextEditor {
    private final LinkedList<Character> chars = new LinkedList<>();
    private ListIterator<Character> cursor;

    TextEditor() { cursor = chars.listIterator(); }

    void type(char c) { cursor.add(c); } // insert before cursor

    void moveCursorLeft()  { if (cursor.hasPrevious()) cursor.previous(); }
    void moveCursorRight() { if (cursor.hasNext()) cursor.next(); }

    void backspace() {
        if (cursor.hasPrevious()) { cursor.previous(); cursor.remove(); }
    }

    String text() {
        StringBuilder sb = new StringBuilder();
        chars.forEach(sb::append);
        return sb.toString();
    }
}

TextEditor ed = new TextEditor();
ed.type('H'); ed.type('e'); ed.type('l'); ed.type('o');
ed.moveCursorLeft(); ed.moveCursorLeft();
ed.type('l');
System.out.println(ed.text()); // Hello

متى تستخدم ListIterator

استخدم ListIterator عندما تحتاج إلى:

  • التنقل إلى الخلف داخل قائمة
  • استبدال العناصر أثناء التنقل باستخدام set()
  • إدراج العناصر أثناء التنقل باستخدام add()
  • تتبع الموضع عبر nextIndex()/previousIndex()

تحقق سريع

ماذا تفعل ListIterator.set()؟

مراجعة: ListIterator والتنقل ثنائي الاتجاه

أهم النقاط:

  • يوسّع ListIterator واجهة Iterator بإضافة hasPrevious() وprevious() وset() وadd()
  • أنشئ ListIterator في موضع محدد باستخدام listIterator(index)
  • تستبدل set() آخر عنصر أرجعته next() أو previous()
  • تُدرج add() العنصر قبل موضع المؤشر الحالي
  • يوفر nextIndex() وpreviousIndex() معلومات عن الموضع
  • استخدم ListIterator مع LinkedList للتنقل والتعديل بتعقيد O(1)

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

هل درس «ListIterator والتنقل ثنائي الاتجاه» مجاني؟

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

ماذا ستتعلم في «ListIterator والتنقل ثنائي الاتجاه»؟

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

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

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

كم من الوقت يستغرق درس «ListIterator والتنقل ثنائي الاتجاه»؟

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

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

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

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

  1. عقود Iterable وIterator
  2. تطبيق Iterator مخصص
  3. ListIterator والتنقل ثنائي الاتجاه
  4. المكررات Fail-Fast مقابل Fail-Safe
← العودة إلى Java Academy