เราสามารถเขียน iterator ของเราเองใน Java ได้หรือไม่?


104

ถ้าฉันมีรายการที่มี[alice, bob, abigail, charlie]และฉันต้องการเขียนตัววนซ้ำเพื่อให้มันวนซ้ำองค์ประกอบที่ขึ้นต้นด้วย 'a' ฉันสามารถเขียนของตัวเองได้หรือไม่ ฉันจะทำเช่นนั้นได้อย่างไร?


4
คุณสามารถ. คุณต้องใช้อินเทอร์เฟซ Iterator
gd1

แน่นอนว่ามันเป็นเพียงอินเทอร์เฟซธรรมดา Proxying java.util สำหรับ JDO im เกี่ยวข้องกับตัวทำซ้ำที่กำหนดเองค่อนข้างมาก
bestsss

คำตอบ:


48

แน่นอน ตัววนซ้ำเป็นเพียงการนำjava.util.Iteratorอินเทอร์เฟซมาใช้ หากคุณกำลังใช้อ็อบเจ็กต์ที่ทำซ้ำได้ที่มีอยู่ (เช่น a LinkedList) จากjava.utilคุณจะต้องทำการคลาสย่อยและแทนที่iteratorฟังก์ชันของมันเพื่อให้คุณส่งคืนของคุณเองหรือระบุวิธีการรวมตัวทำซ้ำมาตรฐานในIteratorอินสแตนซ์พิเศษของคุณ(ซึ่ง มีข้อได้เปรียบในการใช้งานในวงกว้างมากขึ้น) ฯลฯ


8
คำตอบที่ดี .... +1 อย่างไรก็ตามคุณไม่ได้ถูกบังคับให้ใช้ LinkedList คลาสย่อย คุณสามารถเขียน CustomIterator ที่สร้างอินสแตนซ์ด้วย CustomIterator ใหม่ (somelist) เนื่องจากอินเทอร์เฟซไม่ได้บอกอะไรเกี่ยวกับตัวสร้าง
gd1

1
@Giacomo: นั่นคือสิ่งที่ฉันหมายถึง"... หรือให้วิธีการห่อตัววนซ้ำมาตรฐานในIteratorอินสแตนซ์พิเศษของคุณ... " (และขอบคุณ) :-)
TJ Crowder

196

ตัวเลือกที่สามารถใช้ซ้ำได้ดีที่สุดคือการใช้อินเทอร์เฟซที่ทำซ้ำได้และแทนที่ method iterator ()

นี่คือตัวอย่างของ ArrayList เช่นคลาสที่ใช้อินเทอร์เฟซซึ่งคุณจะแทนที่เมธอด Iterator ()

import java.util.Iterator;

public class SOList<Type> implements Iterable<Type> {

    private Type[] arrayList;
    private int currentSize;

    public SOList(Type[] newArray) {
        this.arrayList = newArray;
        this.currentSize = arrayList.length;
    }

    @Override
    public Iterator<Type> iterator() {
        Iterator<Type> it = new Iterator<Type>() {

            private int currentIndex = 0;

            @Override
            public boolean hasNext() {
                return currentIndex < currentSize && arrayList[currentIndex] != null;
            }

            @Override
            public Type next() {
                return arrayList[currentIndex++];
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
        return it;
    }
}

ชั้นนี้การดำเนินการอินเตอร์เฟซที่ใช้ Iterable Generics เมื่อพิจารณาว่าคุณมีองค์ประกอบของอาร์เรย์คุณจะสามารถรับอินสแตนซ์ของ Iterator ซึ่งเป็นอินสแตนซ์ที่จำเป็นที่ใช้โดยลูป "foreach" เป็นต้น

คุณสามารถสร้างอินสแตนซ์แบบไม่ระบุตัวตนของตัววนซ้ำได้โดยไม่ต้องสร้าง Iterator ที่ขยายและใช้ประโยชน์จากค่าของ currentSize เพื่อตรวจสอบว่าคุณสามารถนำทางไปยังตำแหน่งใดได้บ้าง (สมมติว่าคุณสร้างอาร์เรย์ที่มีความจุ 10 แต่คุณมีเพียง 2 องค์ประกอบที่ 0 และ 1) อินสแตนซ์จะมีตัวนับเจ้าของว่ามันอยู่ที่ไหนและสิ่งที่คุณต้องทำคือเล่นกับ hasNext () ซึ่งจะตรวจสอบว่าค่าปัจจุบันไม่ใช่ค่าว่างหรือไม่และค่าถัดไป () ซึ่งจะส่งคืนอินสแตนซ์ของ currentIndex ของคุณ ด้านล่างนี้คือตัวอย่างการใช้ API นี้ ...

public static void main(String[] args) {
    // create an array of type Integer
    Integer[] numbers = new Integer[]{1, 2, 3, 4, 5};

    // create your list and hold the values.
    SOList<Integer> stackOverflowList = new SOList<Integer>(numbers);

    // Since our class SOList is an instance of Iterable, then we can use it on a foreach loop
    for(Integer num : stackOverflowList) {
        System.out.print(num);
    }

    // creating an array of Strings
    String[] languages = new String[]{"C", "C++", "Java", "Python", "Scala"};

    // create your list and hold the values using the same list implementation.
    SOList<String> languagesList = new SOList<String>(languages);

    System.out.println("");
    // Since our class SOList is an instance of Iterable, then we can use it on a foreach loop
    for(String lang : languagesList) {
        System.out.println(lang);
    }
}
// will print "12345
//C
//C++
//Java
//Python
//Scala

หากต้องการคุณสามารถทำซ้ำได้เช่นกันโดยใช้อินสแตนซ์ Iterator:

// navigating the iterator
while (allNumbers.hasNext()) {
    Integer value = allNumbers.next();
    if (allNumbers.hasNext()) {
        System.out.print(value + ", ");
    } else {
        System.out.print(value);
    }
} 
// will print 1, 2, 3, 4, 5

เอกสาร foreach ตั้งอยู่ที่http://download.oracle.com/javase/1,5.0/docs/guide/language/foreach.html คุณสามารถที่จะดูที่การใช้งานที่สมบูรณ์มากขึ้นในการปฏิบัติส่วนตัวของฉันรหัส Google

ตอนนี้เพื่อให้ได้เอฟเฟกต์ของสิ่งที่คุณต้องการฉันคิดว่าคุณต้องเสียบแนวคิดของตัวกรองใน Iterator ... เนื่องจากตัววนซ้ำขึ้นอยู่กับค่าถัดไปจึงยากที่จะคืนค่าจริงใน hasNext () แล้ว กรองการใช้งาน next () ด้วยค่าที่ไม่ได้ขึ้นต้นด้วยอักขระ "a" เช่น ฉันคิดว่าคุณต้องเล่นกับผู้โต้ตอบรองตามรายการที่กรองด้วยค่าด้วยตัวกรองที่กำหนด


14
for instanceนั่นคือการเล่นสำนวน?
n611x007

4
อีก 30 คนไม่คิดว่านั่นเป็นการเล่นสำนวน :)
Marcello de Sales

2
เป็นแนวทางปฏิบัติที่ดีในการยกเลิกข้อยกเว้นการดำเนินการที่ไม่ได้รับการสนับสนุนจากเรา ฉันคิดว่าเป็นความคิดที่ดีที่จะโยนข้อยกเว้นการดำเนินการที่ไม่รองรับออกจากวิธีลบ ()!
darshan

2
ขออภัย @darshan แต่วิธีแก้ปัญหานี้เกี่ยวข้องกับ "วิธีการเขียนตัววนซ้ำ" ... หากโฟกัสคือ "เขียนโค้ดที่เขียนได้อย่างสมบูรณ์แบบ" ก็จะมี!
Marcello de Sales

ไม่ชัดเจนว่าทำไมต้องมีการตรวจสอบ 'arrayList [currentIndex]! = null' ภายใน hasNext () ใครช่วยอธิบายหน่อย
Bhushan Karmarkar

12

ตัวอย่างที่ดีสำหรับ Iterable เพื่อคำนวณแฟกทอเรียล

FactorialIterable fi = new FactorialIterable(10);
Iterator<Integer> iterator = fi.iterator();
while (iterator.hasNext()){
     System.out.println(iterator.next());
}

รหัสย่อสำหรับ Java 1.8

new FactorialIterable(5).forEach(System.out::println);

คลาสที่ทำซ้ำได้เอง

public class FactorialIterable implements Iterable<Integer> {

    private final FactorialIterator factorialIterator;

    public FactorialIterable(Integer value) {
        factorialIterator = new FactorialIterator(value);
    }

    @Override
    public Iterator<Integer> iterator() {
        return factorialIterator;
    }

    @Override
    public void forEach(Consumer<? super Integer> action) {
        Objects.requireNonNull(action);
        Integer last = 0;
        for (Integer t : this) {
            last = t;
        }
        action.accept(last);
    }

}

คลาส Iterator ที่กำหนดเอง

public class FactorialIterator implements Iterator<Integer> {

    private final Integer mNumber;
    private Integer mPosition;
    private Integer mFactorial;


    public FactorialIterator(Integer number) {
        this.mNumber = number;
        this.mPosition = 1;
        this.mFactorial = 1;
    }

    @Override
    public boolean hasNext() {
        return mPosition <= mNumber;
    }

    @Override
    public Integer next() {
        if (!hasNext())
            return 0;

        mFactorial = mFactorial * mPosition;

        mPosition++;

        return  mFactorial;
    }
}

8

นี่คือรหัสที่สมบูรณ์ในการเขียนตัววนซ้ำเพื่อให้มันวนซ้ำองค์ประกอบที่ขึ้นต้นด้วย 'a':

import java.util.Iterator;

public class AppDemo {

    public static void main(String args[]) {

        Bag<String> bag1 = new Bag<>();

        bag1.add("alice");
        bag1.add("bob"); 
        bag1.add("abigail");
        bag1.add("charlie"); 

        for (Iterator<String> it1 = bag1.iterator(); it1.hasNext();) {

            String s = it1.next();
            if (s != null)
                System.out.println(s); 
        }
    }
}

คลาส Iterator ที่กำหนดเอง

import java.util.ArrayList;
import java.util.Iterator;

public class Bag<T> {

    private ArrayList<T> data;

    public Bag() {

        data = new ArrayList<>();
    }

    public void add(T e) {

        data.add(e); 
    }

    public Iterator<T> iterator() {

        return new BagIterator();
    }

    public class BagIterator<T> implements Iterator<T> {

        private int index; 
        private String str;

        public BagIterator() {

            index = 0;
        }

        @Override
        public boolean hasNext() {

             return index < data.size();  
        }

        @Override
        public T next() {

            str = (String) data.get(index); 
            if (str.startsWith("a"))
                return (T) data.get(index++); 
            index++; 
            return null; 
        }
    } 
}

5

คุณสามารถใช้ Iterator ของคุณเองได้ ตัววนซ้ำของคุณสามารถสร้างขึ้นเพื่อห่อ Iterator ที่ส่งคืนโดย List หรือคุณสามารถเก็บเคอร์เซอร์ไว้และใช้วิธี List's get (int index) คุณเพียงแค่ต้องเพิ่มตรรกะในวิธีการถัดไปของ Iterator ของคุณและวิธี hasNext เพื่อพิจารณาเกณฑ์การกรองของคุณ คุณจะต้องตัดสินใจด้วยว่าตัววนซ้ำของคุณจะรองรับการลบหรือไม่


1

นี่คือคำตอบที่สมบูรณ์สำหรับคำถาม

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

class ListIterator implements Iterator<String>{
    List<String> list;
    int pos = 0;

    public ListIterator(List<String> list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        while(pos < list.size()){
            if (list.get(pos).startsWith("a"))
                return true;
            pos++;
        }
        return false;

    }

    @Override
    public String next() {
        if (hasNext())
            return list.get(pos++);
        throw new NoSuchElementException();
    }
}

public class IteratorTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("alice", "bob", "abigail", "charlie");
        ListIterator itr = new ListIterator(list);

        while(itr.hasNext())
            System.out.println(itr.next()); // prints alice, abigail
    }
}
  • ListIterator เป็นตัววนซ้ำสำหรับอาร์เรย์ซึ่งส่งคืนองค์ประกอบที่ขึ้นต้นด้วย 'a'
  • ไม่จำเป็นต้องใช้อินเทอร์เฟซที่ทำซ้ำได้ แต่นั่นคือความเป็นไปได้
  • ไม่จำเป็นต้องใช้สิ่งนี้โดยทั่วไป
  • เป็นไปตามสัญญาสำหรับ hasNext () และ next () อย่างสมบูรณ์ เช่น if hasNext () บอกว่ายังมีองค์ประกอบ next () จะส่งคืนองค์ประกอบเหล่านั้น และถ้า hasNext () บอกว่าไม่มีองค์ประกอบอีกต่อไปก็จะส่งคืนNoSuchElementExceptionข้อยกเว้นที่ถูกต้อง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.