"Chào, Amigo!"

"Chào, Ellie!"

"Hôm nay tôi muốn nói với bạn về iterators."

"Các trình lặp được phát minh trên thực tế cùng lúc với các bộ sưu tập. Mục đích chính của các bộ sưu tập là lưu trữ các phần tử và mục đích chính của một trình vòng lặp là truy xuất từng phần tử này."

"Có gì khó khăn để có được một tập hợp các yếu tố?"

"Đầu tiên, các phần tử trong một số bộ sưu tập, chẳng hạn như Tập hợp, không có thứ tự được thiết lập và/hoặc thứ tự thay đổi liên tục."

"Thứ hai, một số cấu trúc dữ liệu có thể lưu trữ các đối tượng theo một cách rất phức tạp: trong các nhóm, danh sách khác nhau, v.v. Nói cách khác, phân phát tất cả các phần tử theo thứ tự sẽ là một nhiệm vụ không hề nhỏ."

"Thứ ba, các bộ sưu tập có xu hướng thay đổi. Giả sử bạn quyết định hiển thị toàn bộ nội dung của một bộ sưu tập, nhưng ngay ở giữa đầu ra, JVM chuyển sang một luồng khác thay thế một nửa phần tử của bộ sưu tập. Vì vậy, thay vì đầu ra, bạn nhận được ai mà biết được."

"Hừm..."

"Nhưng! Đây chính xác là loại vấn đề mà trình vòng lặp có thể giải quyết. Trình vòng lặp là một đối tượng đặc biệt trong một bộ sưu tập, một mặt, có quyền truy cập vào tất cả dữ liệu riêng tư của nó và biết cấu trúc bên trong của nó, mặt khác , triển khai giao diện Iterator công khai, cho phép mọi người biết cách làm việc với nó. "

"Một số trình vòng lặp có một mảng bên trong mà tất cả các phần tử của bộ sưu tập được sao chép vào đó khi trình vòng lặp được tạo. Điều này đảm bảo rằng mọi thay đổi tiếp theo đối với bộ sưu tập sẽ không ảnh hưởng đến số lượng hoặc thứ tự của các phần tử."

"Tôi nghĩ bạn đã gặp phải vấn đề này khi làm việc với for each . Bạn không thể đồng thời lặp qua một bộ sưu tập và xóa các phần tử khỏi nó. Tất cả là do cách thức hoạt động của trình vòng lặp."

"Trong các bộ sưu tập mới được thêm vào thư viện đồng thời, trình vòng lặp được làm lại để loại bỏ vấn đề này."

"Hãy để tôi nhắc bạn cách một iterator hoạt động."

"Java có giao diện Iterator đặc biệt. Đây là các phương thức của nó:"

Các phương thức của giao diện Iterator<E> Sự miêu tả
boolean hasNext() Kiểm tra xem có thêm phần tử nào không
E next() Trả về phần tử hiện tại và di chuyển đến phần tử tiếp theo.
void remove() Xóa phần tử hiện tại

"Trình lặp cho phép bạn nhận liên tiếp tất cả các phần tử của một bộ sưu tập. Sẽ hợp lý hơn khi coi trình vòng lặp giống như một InputStream — nó có tất cả dữ liệu, nhưng nhiệm vụ của nó là xuất dữ liệu một cách tuần tự."

"   Phương thức next () trả về phần tử tiếp theo trong bộ sưu tập."

" Phương thức hasNext () dùng để kiểm tra xem còn phần tử nào nữa không."

"Và remove () xóa phần tử hiện tại."

"Có câu hỏi nào không?"

"Tại sao các phương thức lại có tên lạ như vậy? Tại sao không phải là isEmpty() và getNextElement()?"

"Điều đó sẽ không có ý nghĩa hơn?"

"Điều đó sẽ hợp lý hơn, nhưng những cái tên này đến từ ngôn ngữ C++, nơi mà các trình vòng lặp đã xuất hiện sớm hơn."

"Tôi hiểu rồi. Tiếp tục nào."

"Ngoài trình vòng lặp, còn có giao diện Iterable, giao diện này phải được triển khai bởi tất cả các bộ sưu tập hỗ trợ trình vòng lặp. Nó có một phương thức duy nhất:"

Các phương thức của giao diện Iterable<T> Sự miêu tả
Iterator<T>iterator() Trả về một đối tượng iterator

"Bạn có thể sử dụng phương pháp này trên bất kỳ bộ sưu tập nào để có được một đối tượng iterator duyệt qua các phần tử của nó. Hãy duyệt qua tất cả các phần tử của trong TreeSet : "

Ví dụ
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}

"Sử dụng một trình vòng lặp như thế này không thuận tiện lắm — có quá nhiều mã thừa và rõ ràng. Tình hình trở nên đơn giản hơn khi vòng lặp for-each xuất hiện trong Java."

"Bây giờ mã này nhỏ gọn và dễ đọc hơn nhiều:"

Trước Sau đó
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}
TreeSet<String> set = new TreeSet<String>();

for(String item : set)
{
 System.out.println(item);
}

"Đây là cùng một mã! Trình vòng lặp được sử dụng trong cả hai trường hợp."

"Chỉ là việc sử dụng nó bị ẩn trong vòng lặp for-each . Lưu ý rằng đoạn mã bên phải hoàn toàn không có chữ màu đỏ . Việc sử dụng trình vòng lặp hoàn toàn bị ẩn."

"Một vòng lặp for-each được sử dụng cho bất kỳ đối tượng nào hỗ trợ trình vòng lặp. Nói cách khác, bạn có thể viết lớp của riêng mình, thêm phương thức iterator () vào nó và sử dụng các đối tượng của nó trong cấu trúc for-each ."

"Chà! Tất nhiên, tôi không háo hức viết các bộ sưu tập và trình vòng lặp của riêng mình, nhưng triển vọng vẫn rất hấp dẫn. Tôi sẽ ghi lại điều đó."

Ngoài ra, có một loại trình vòng lặp phổ biến khác thậm chí còn có giao diện riêng. Tôi đang nói về một trình lặp cho danh sách, tức là ListIterator .

"Bất kể việc triển khai chúng như thế nào, các danh sách vẫn duy trì thứ tự của các phần tử, giúp làm việc với chúng thông qua một trình vòng lặp thuận tiện hơn một chút."

"Đây là các phương thức của giao diện ListIterator <E>:"

Phương pháp Sự miêu tả
boolean hasNext() Kiểm tra xem có thêm yếu tố nào phía trước không.
E next() Trả về phần tử tiếp theo.
int nextIndex() Trả về chỉ số của phần tử tiếp theo
void set(E e) Thay đổi giá trị của phần tử hiện tại
boolean hasPrevious() Kiểm tra nếu có bất kỳ yếu tố đằng sau.
E previous() Trả về phần tử trước đó
int previousIndex() Trả về chỉ mục của phần tử trước đó
void remove() Xóa phần tử hiện tại
void add(E e) Thêm một phần tử vào cuối danh sách.

"Nói cách khác, ở đây chúng ta có thể di chuyển cả về phía trước và phía sau. Và có một số tính năng nhỏ khác."

"Chà, đó là thứ thú vị. Nó được sử dụng ở đâu?"

"Giả sử bạn muốn di chuyển qua lại trong danh sách được liên kết. Thao tác nhận sẽ khá chậm, nhưng thao tác next() sẽ rất nhanh."

"Hmm. Bạn đã thuyết phục tôi. Tôi sẽ ghi nhớ điều đó."

"Cảm ơn, Ellie!"