0Pricing
Java Academy · درس

تطبيق Iterator مخصص

أنشئ فئة Iterator مخصصة لقائمة مترابطة بسيطة أو لبنية نطاق

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

Iterator مخصص

يمنحك إنشاء Iterator مخصص تحكمًا كاملًا في كيفية اجتياز بنية البيانات. يشرح هذا الدرس تطبيق Iterator لقائمة مرتبطة خطوةً بخطوة.

فئة Node

أولًا، عرّف بنية العقدة لقائمة مرتبطة أحادية الاتجاه.

class Node<T> {
    final T value;
    Node<T> next;

    Node(T value) {
        this.value = value;
    }
}

// Building a chain: 1 -> 2 -> 3
Node<Integer> head = new Node<>(1);
head.next = new Node<>(2);
head.next.next = new Node<>(3);

تطبيق Iterator

أنشئ فئة داخلية تطبّق Iterator<T>، مع مؤشر يشير إلى العقدة الحالية.

import java.util.Iterator;
import java.util.NoSuchElementException;

class LinkedList<T> implements Iterable<T> {
    private Node<T> head;
    private int size;

    private class LinkedListIterator implements Iterator<T> {
        private Node<T> current = head; // cursor

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public T next() {
            if (!hasNext()) throw new NoSuchElementException();
            T value = current.value;
            current = current.next;
            return value;
        }
    }

    @Override
    public Iterator<T> iterator() {
        return new LinkedListIterator();
    }
}

addFirst وLinkedList المكتملة

أضف إمكانية إدراج العقد في البداية، واطّلع على الفئة العاملة بالكامل.

class LinkedList<T> implements Iterable<T> {
    private Node<T> head;
    private int size;

    public void addFirst(T value) {
        Node<T> node = new Node<>(value);
        node.next = head;
        head = node;
        size++;
    }

    public void addLast(T value) {
        Node<T> node = new Node<>(value);
        if (head == null) { head = node; }
        else {
            Node<T> curr = head;
            while (curr.next != null) curr = curr.next;
            curr.next = node;
        }
        size++;
    }

    public int size() { return size; }

    @Override
    public Iterator<T> iterator() {
        return new LinkedListIterator();
    }
}

استخدام Iterator المخصص

بعد تطبيق واجهة Iterable، تعمل القائمة المرتبطة في حلقات for-each ومع forEach.

LinkedList<String> list = new LinkedList<>();
list.addLast("Alice");
list.addLast("Bob");
list.addLast("Charlie");

// For-each loop works!
for (String name : list) {
    System.out.println(name);
}
// Alice
// Bob
// Charlie

// Stream also works (Java 8+)
list.forEach(name -> System.out.println("Hello, " + name));

Iterator للنطاق

مثال أبسط: Iterator على نطاق رقمي دون بنية بيانات داعمة.

class IntRange implements Iterable<Integer> {
    private final int start, end, step;

    IntRange(int start, int end, int step) {
        this.start = start; this.end = end; this.step = step;
    }
    IntRange(int start, int end) { this(start, end, 1); }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<>() {
            int current = start;
            public boolean hasNext() { return current < end; }
            public Integer next() {
                if (!hasNext()) throw new NoSuchElementException();
                int val = current;
                current += step;
                return val;
            }
        };
    }
}

for (int n : new IntRange(0, 10, 2)) System.out.print(n + " ");
// 0 2 4 6 8

Iterator للمرور الوسطي في شجرة

تطبيق Iterator للمرور الوسطي في BST باستخدام مكدس صريح — يوضّح كيف يمكن للمكرّرات أن تحل محل الاجتياز التعاودي.

import java.util.*;

class BinaryTree<T extends Comparable<T>> {
    private record TreeNode<T>(T val, TreeNode<T> left, TreeNode<T> right) {}

    private TreeNode<T> root;

    public Iterator<T> inorderIterator() {
        Deque<TreeNode<T>> stack = new ArrayDeque<>();
        pushLeft(root, stack);
        return new Iterator<>() {
            public boolean hasNext() { return !stack.isEmpty(); }
            public T next() {
                TreeNode<T> node = stack.pop();
                pushLeft(node.right(), stack);
                return node.val();
            }
        };
    }

    private void pushLeft(TreeNode<T> node, Deque<TreeNode<T>> stack) {
        while (node != null) { stack.push(node); node = node.left(); }
    }
}

Iterator كسول

يمكن للمكرّرات توليد القيم بشكل كسول — أي عند استدعاء next() فقط. وهذا مفيد للتسلسلات اللانهائية.

class FibonacciIterator implements Iterator<Long> {
    private long a = 0, b = 1;

    @Override public boolean hasNext() { return true; } // infinite!

    @Override public Long next() {
        long result = a;
        long next = a + b;
        a = b;
        b = next;
        return result;
    }
}

Iterator<Long> fib = new FibonacciIterator();
for (int i = 0; i < 10; i++) System.out.print(fib.next() + " ");
// 0 1 1 2 3 5 8 13 21 34

Iterator للتصفية

Iterator مزخرف يغلّف Iterator آخر ويتجاوز العناصر التي لا تطابق مسندًا.

import java.util.*;
import java.util.function.*;

class FilterIterator<T> implements Iterator<T> {
    private final Iterator<T> source;
    private final Predicate<T> predicate;
    private T next;
    private boolean hasNext;

    FilterIterator(Iterator<T> source, Predicate<T> predicate) {
        this.source = source; this.predicate = predicate;
        advance();
    }

    private void advance() {
        hasNext = false;
        while (source.hasNext()) {
            T candidate = source.next();
            if (predicate.test(candidate)) { next = candidate; hasNext = true; break; }
        }
    }

    public boolean hasNext() { return hasNext; }
    public T next() { T val = next; advance(); return val; }
}

List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,10);
Iterator<Integer> evens = new FilterIterator<>(nums.iterator(), n -> n % 2 == 0);
while (evens.hasNext()) System.out.print(evens.next() + " ");
// 2 4 6 8 10

تكامل Iterator مع Stream

يمكن تكييف المكرّرات للعمل مع Streams باستخدام Spliterators.spliteratorUnknownSize().

import java.util.*;
import java.util.stream.*;

Iterator<Integer> rangeIt = new IntRange(1, 6).iterator();

Stream<Integer> stream = StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(rangeIt, Spliterator.ORDERED),
    false // not parallel
);

int sum = stream.mapToInt(Integer::intValue).sum();
System.out.println(sum); // 15

الإزالة أثناء التكرار

يزيل الأسلوب الاختياري remove() في Iterator العنصر الذي أعاده آخر استدعاء لـ next() — ويجب تطبيقه صراحةً في المكرّرات المخصصة.

class MutableLinkedList<T> implements Iterable<T> {
    // ... (full implementation)

    // Iterator with remove support
    private class RemovableIterator implements Iterator<T> {
        private Node<T> prev = null;
        private Node<T> current = head;

        public boolean hasNext() { return current != null; }
        public T next() {
            prev = (prev == null) ? null : current;
            T val = current.value;
            current = current.next;
            return val;
        }

        public void remove() {
            // Remove the last returned node
            if (prev == null) head = current;
            else prev.next = current;
            size--;
        }
    }
}

قائمة التحقق الخاصة بـ Iterator

عند تطبيق Iterator مخصص:

  • استدعِ hasNext() دائمًا قبل next()
  • اطرح NoSuchElementException من next() عند عدم وجود عناصر، ولا تُعِد null
  • اجعل المكرّر عديم الحالة بالنسبة إلى المجموعة (لا تخزّن حجم المجموعة مؤقتًا)
  • استخدم modCount لاكتشاف التعديل المتزامن عند الحاجة

تحقق سريع

ما الذي ينبغي أن يطرحه next() عند عدم وجود عناصر أخرى؟

مراجعة: تطبيق Iterator مخصص

أهم النقاط:

  • طبّق Iterator باستخدام hasNext() وnext() وremove() الاختياري
  • احتفظ بحقل مؤشر في المكرّر يشير إلى العنصر التالي
  • اطرح NoSuchElementException من next() عندما تكون hasNext() تساوي false
  • أنشئ مثيل Iterator جديدًا لكل استدعاء لـ iterator() للحصول على مؤشرات مستقلة
  • تولّد المكرّرات الكسولة القيم عند الطلب — وهو مفيد للتسلسلات اللانهائية
  • غلّف المكرّرات في StreamSupport.stream() لربطها بواجهة Stream البرمجية

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

هل درس «تطبيق Iterator مخصص» مجاني؟

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

ماذا ستتعلم في «تطبيق Iterator مخصص»؟

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

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

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

كم من الوقت يستغرق درس «تطبيق Iterator مخصص»؟

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

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

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

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

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