Chúng ta có thể viết trình vòng lặp của riêng mình trong Java không?


104

Nếu tôi có một danh sách chứa [alice, bob, abigail, charlie]và tôi muốn viết một trình lặp sao cho nó lặp qua các phần tử bắt đầu bằng 'a', tôi có thể viết một trình lặp của riêng mình không? Làm thế nào tôi có thể làm điều đó ?


4
Bạn có thể. Bạn phải triển khai giao diện Iterator.
gd1

chắc chắn, nó chỉ là một giao diện bình thường. Proxy java.util để cấy ghép JDO. liên quan đến khá nhiều trình lặp tùy chỉnh.
bestsss

Câu trả lời:


48

Chắc chắn rồi. Một trình lặp chỉ là một triển khai của java.util.Iteratorgiao diện. Nếu bạn đang sử dụng một đối tượng có thể lặp lại hiện tại (giả sử, a LinkedList) từ java.util, bạn sẽ cần phải phân lớp nó và ghi đè iteratorchức năng của nó để bạn trả về đối tượng của riêng mình hoặc cung cấp một phương tiện để gói một trình lặp tiêu chuẩn trong phiên bản đặc biệt của bạn Iterator(mà có lợi thế là được sử dụng rộng rãi hơn), v.v.


8
câu trả lời hay .... +1 Tuy nhiên, bạn không bị buộc phải phân lớp LinkedList. Bạn có thể viết một CustomIterator mà nó được khởi tạo với CustomIterator mới (somelist), vì các giao diện không nói gì về các hàm tạo.
gd1

1
@Giacomo: Đó là ý của tôi khi "... hoặc cung cấp một phương tiện để gói một trình lặp tiêu chuẩn trong Iteratortrường hợp đặc biệt của bạn ..." (và cảm ơn). :-)
TJ Crowder

196

Tùy chọn có thể tái sử dụng tốt nhất là triển khai giao diện Có thể lặp lại và ghi đè phương thức iterator ().

Đây là một ví dụ về một lớp ArrayList như triển khai giao diện, trong đó bạn ghi đè phương thức 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;
    }
}

Lớp này thực hiện giao diện có thể lặp lại bằng cách sử dụng Generics . Xem xét bạn có các phần tử trong mảng, bạn sẽ có thể nhận được một phiên bản của một Iterator, ví dụ: là phiên bản cần thiết được sử dụng bởi vòng lặp "foreach".

Bạn chỉ có thể tạo một phiên bản ẩn danh của trình vòng lặp mà không cần tạo Trình lặp mở rộng và tận dụng giá trị của currentSize để xác minh nơi bạn có thể điều hướng trên mảng (giả sử bạn đã tạo một mảng có dung lượng là 10, nhưng bạn chỉ có 2 phần tử 0 và 1). Phiên bản sẽ có bộ đếm chủ sở hữu của nó về vị trí của nó và tất cả những gì bạn cần làm là chơi với hasNext (), xác minh xem giá trị hiện tại có phải là null hay không và tiếp theo (), sẽ trả về phiên bản currentIndex của bạn. Dưới đây là một ví dụ về việc sử dụng API này ...

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

Nếu muốn, bạn cũng có thể lặp lại nó bằng cách sử dụng thể hiện 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

Tài liệu foreach có tại http://download.oracle.com/javase/1,5.0/docs/guide/language/foreach.html . Bạn có thể xem cách triển khai đầy đủ hơn tại mã google thực hành cá nhân của tôi .

Bây giờ, để có được những hiệu ứng của những gì bạn cần, tôi nghĩ bạn cần phải cắm một khái niệm về bộ lọc trong Iterator ... Vì trình lặp phụ thuộc vào các giá trị tiếp theo, sẽ khó trả về true trên hasNext (), và sau đó lọc triển khai next () với giá trị không bắt đầu bằng char "a" chẳng hạn. Tôi nghĩ bạn cần phải thử với một Interator phụ dựa trên một danh sách đã lọc với các giá trị với bộ lọc đã cho.


14
for instance, đó có phải là một cách chơi chữ?
n611x007

4
30 người khác đã không nghĩ rằng đó là một sự chơi chữ :)
Marcello de Sales

2
Đó là một thực tiễn tốt để loại bỏ ngoại lệ hoạt động không được hỗ trợ từ các phương pháp đã triển khai của chúng tôi. Tôi nghĩ bạn nên ném ngoại lệ hoạt động không được hỗ trợ khỏi phương thức remove ()!
darshan

2
Xin lỗi @darshan, nhưng giải pháp này liên quan đến "cách viết trình vòng lặp" ... Nếu trọng tâm là "viết mã được viết hoàn hảo", điều đó sẽ ở đó!
Marcello de Sales,

không rõ tại sao cần kiểm tra 'arrayList [currentIndex]! = null' bên trong hasNext (). ai đó có thể vui lòng giải thích.
Bhushan Karmarkar

12

Ví dụ tốt cho Iterable để tính giai thừa

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

Mã ngắn cho Java 1.8

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

Lớp có thể lặp lại tùy chỉnh

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

}

Lớp Trình lặp tùy chỉnh

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

Đây là mã hoàn chỉnh để viết một trình lặp để nó lặp qua các phần tử bắt đầu bằng '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); 
        }
    }
}

Lớp Trình lặp tùy chỉnh

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

Bạn có thể triển khai Iterator của riêng mình. Trình vòng lặp của bạn có thể được xây dựng để bao bọc Trình lặp lại do Danh sách trả về hoặc bạn có thể giữ một con trỏ và sử dụng phương thức get (int index) của Danh sách. Bạn chỉ cần thêm logic vào phương thức tiếp theo của Iterator VÀ phương thức hasNext để tính đến tiêu chí lọc của bạn. Bạn cũng sẽ phải quyết định xem trình lặp của mình có hỗ trợ thao tác xóa hay không.


1

Đây là câu trả lời đầy đủ cho câu hỏi.

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 là trình lặp cho mảng trả về các phần tử bắt đầu bằng 'a'.
  • Không cần triển khai giao diện có thể lặp lại. Nhưng đó là một khả năng.
  • Không cần phải thực hiện điều này một cách chung chung.
  • Nó hoàn toàn đáp ứng hợp đồng cho hasNext () và next (). tức là nếu hasNext () cho biết vẫn còn các phần tử, thì next () sẽ trả về các phần tử đó. Và nếu hasNext () cho biết không còn phần tử nào nữa, nó sẽ trả về một NoSuchElementExceptionngoại lệ hợp lệ.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.