Bisakah kita menulis iterator kita sendiri di Java?

104

Jika saya memiliki daftar yang berisi [alice, bob, abigail, charlie]dan saya ingin menulis iterator sedemikian rupa sehingga mengulangi elemen yang dimulai dengan 'a', dapatkah saya menulis milik saya sendiri? Bagaimana saya bisa melakukan itu?

burung phoenix
sumber
4
Kamu bisa. Anda harus menerapkan antarmuka Iterator.
gd1
tentu, ini hanya antarmuka biasa. Proxying java.util untuk implan JDO. melibatkan cukup banyak iterator khusus.
bestsss
codereview.stackexchange.com/questions/48109/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:

48

Tentu. Sebuah iterator hanyalah implementasi dari java.util.Iteratorantarmuka. Jika Anda menggunakan objek iterable yang sudah ada (katakanlah, a LinkedList) dari java.util, Anda harus membuat subkelasnya dan mengganti iteratorfungsinya sehingga Anda mengembalikan milik Anda sendiri, atau menyediakan sarana untuk membungkus iterator standar dalam Iteratorcontoh khusus Anda (yang mana memiliki keuntungan karena digunakan secara lebih luas), dll.

TJ Crowder
sumber
8
jawaban yang bagus .... +1 Namun Anda tidak dipaksa untuk subclass LinkedList. Anda dapat menulis CustomIterator yang dibuat dengan CustomIterator baru (somelist), karena antarmuka tidak memberi tahu apa pun tentang konstruktor.
gd1
1
@Giacomo: Itulah yang saya maksud dengan "... atau menyediakan sarana untuk membungkus iterator standar dalam Iteratorcontoh khusus Anda ..." (dan terima kasih). :-)
TJ Crowder
196

Opsi terbaik yang dapat digunakan kembali adalah dengan mengimplementasikan antarmuka Iterable dan mengganti metode iterator ().

Berikut adalah contoh kelas ArrayList yang mengimplementasikan antarmuka, di mana Anda mengganti metode 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;
    }
}

Kelas ini mengimplementasikan antarmuka Iterable menggunakan Generik . Mengingat Anda memiliki elemen pada array, Anda akan bisa mendapatkan instance dari Iterator, yang merupakan instance yang diperlukan yang digunakan oleh loop "foreach", misalnya.

Anda bisa membuat instance anonim dari iterator tanpa membuat perpanjangan Iterator dan memanfaatkan nilai currentSize untuk memverifikasi hingga ke tempat Anda dapat menavigasi array (katakanlah Anda membuat array dengan kapasitas 10, tetapi Anda hanya memiliki 2 elemen pada 0 dan 1). Instance akan memiliki penghitung pemiliknya di mana ia berada dan yang perlu Anda lakukan hanyalah bermain dengan hasNext (), yang memverifikasi apakah nilai saat ini bukan null, dan next (), yang akan mengembalikan instance dari currentIndex Anda. Di bawah ini adalah contoh penggunaan API ini ...

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

Jika mau, Anda juga dapat mengulanginya menggunakan instance 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

Dokumentasi foreach terletak di http://download.oracle.com/javase/1,5.0/docs/guide/language/foreach.html . Anda dapat melihat implementasi yang lebih lengkap di kode google praktik pribadi saya .

Sekarang, untuk mendapatkan efek dari apa yang Anda butuhkan, saya pikir Anda perlu memasukkan konsep filter di Iterator ... Karena iterator bergantung pada nilai berikutnya, akan sulit untuk mengembalikan true pada hasNext (), dan kemudian filter implementasi next () dengan nilai yang tidak dimulai dengan karakter "a" misalnya. Saya pikir Anda perlu bermain-main dengan Interator sekunder berdasarkan daftar yang difilter dengan nilai-nilai dengan filter yang diberikan.

Marcello de Sales
sumber
14
for instance, apakah itu pelesetan?
n611x007
4
30 orang lainnya tidak berpikir itu pelesetan :)
Marcello de Sales
2
Merupakan praktik yang baik untuk membuang pengecualian operasi yang tidak didukung dari metode yang kami implementasikan. Saya pikir itu ide yang baik untuk membuang pengecualian operasi yang tidak didukung dari metode remove ()!
darshan
2
Maaf @darshan, tetapi solusi ini berkaitan dengan "cara menulis iterator" ... Jika fokusnya adalah "menulis kode yang ditulis dengan sempurna", itu pasti ada!
Marcello de Sales
tidak jelas mengapa pemeriksaan 'arrayList [currentIndex]! = null' diperlukan di dalam hasNext (). dapatkah seseorang menjelaskan.
Bhushan Karmarkar
12

Contoh yang bagus untuk Iterable untuk menghitung faktorial

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

Kode pendek untuk Java 1.8

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

Kelas Iterable kustom

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);
    }

}

Kelas Iterator khusus

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;
    }
}
Vahe Gharibyan
sumber
8

Ini adalah kode lengkap untuk menulis iterator sedemikian rupa sehingga iterasi elemen yang dimulai dengan '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); 
        }
    }
}

Kelas Iterator khusus

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; 
        }
    } 
}
elvis
sumber
5

Anda dapat menerapkan Iterator Anda sendiri. Iterator Anda dapat dibuat untuk membungkus Iterator yang dikembalikan oleh List, atau Anda dapat menyimpan kursor dan menggunakan metode get (int index) List. Anda hanya perlu menambahkan logika ke metode Iterator Anda berikutnya DAN metode hasNext untuk memperhitungkan kriteria pemfilteran Anda. Anda juga harus memutuskan apakah iterator Anda akan mendukung operasi penghapusan.

membuang
sumber
1

Inilah jawaban lengkap dari pertanyaan tersebut.

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 adalah iterator untuk larik yang mengembalikan elemen yang dimulai dengan 'a'.
  • Tidak perlu mengimplementasikan antarmuka Iterable. Tapi itu kemungkinan.
  • Tidak perlu menerapkan ini secara umum.
  • Ini sepenuhnya memenuhi kontrak untuk hasNext () dan next (). yaitu jika hasNext () mengatakan masih ada elemen, next () akan mengembalikan elemen tersebut. Dan jika hasNext () mengatakan tidak ada lagi elemen, itu mengembalikan NoSuchElementExceptionpengecualian yang valid .
apadana
sumber