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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- عقود Iterable وIterator
- تطبيق Iterator مخصص
- ListIterator والتنقل ثنائي الاتجاه
- المكررات Fail-Fast مقابل Fail-Safe