Như chúng ta đã biết, Java có bốn cursors: Enumeration, Iterator, ListIterator, và Spliterator. Trong bài viết trước, chúng ta đã thảo luận về Enumeration và Iterator cursors. Ở bài viết này, chúng ta sẽ tập trung tìm hiểu về cursor thứ ba: Java ListIterator.
Java ListIterator là gì?
Giống như Iterator, ListIterator là một Java Iterator, được sử dụng để duyệt qua các phần tử lần lượt từng cái một từ một đối tượng được triển khai từ List.
- Nó có sẵn từ Java 1.2.
- Nó mở rộng từ Iterator interface.
- Nó chỉ hữu ích cho các class được triển khai từ List.
- Khác với Iterator, nó hỗ trợ đầy đủ bốn thao tác CRUD (CREATE, READ, UPDATE và DELETE).
- Khác với Iterator, nó hỗ trợ cả duyệt tiến (Forward Direction) và duyệt lùi (Backward Direction).
- Nó là một Bi-directional Iterator.
- Nó không có phần tử hiện tại; vị trí con trỏ của nó luôn nằm giữa phần tử sẽ được trả về bởi lệnh gọi previous() và phần tử sẽ được trả về bởi lệnh gọi next().
GHI CHÚ:– CRUD operations trong Collection API là gì?
- CREATE: Thêm phần tử mới vào đối tượng Collection.
- READ: Truy xuất phần tử từ đối tượng Collection.
- UPDATE: Cập nhật hoặc thiết lập lại các phần tử đã có trong đối tượng Collection.
- DELETE: Xóa bỏ phần tử khỏi đối tượng Collection.
Sơ đồ lớp ListIterator trong Java
Trong Java, ListIterator là một interface trong Collection API. Nó kế thừa từ interface Iterator. Để hỗ trợ duyệt theo cả hai chiều (tiến và lùi) và các thao tác CRUD, nó có những phương thức sau. Chúng ta có thể sử dụng Iterator này cho tất cả các lớp triển khai List như ArrayList, CopyOnWriteArrayList, LinkedList, Stack, Vector, v.v.
Chúng ta sẽ khám phá chi tiết các phương thức này cùng một số phương thức hữu ích ở các phần tiếp theo.
Các phương thức của Java ListIterator
Interface ListIterator trong Java có các phương thức sau.
- void add(E e): Chèn phần tử được chỉ định vào danh sách.
- boolean hasNext(): Trả về true nếu iterator còn phần tử khi duyệt danh sách theo chiều tiến.
- boolean hasPrevious(): Trả về true nếu iterator còn phần tử khi duyệt danh sách theo chiều ngược lại.
- E next(): Trả về phần tử kế tiếp trong danh sách và tiến vị trí con trỏ.
- int nextIndex(): Trả về chỉ số của phần tử sẽ được trả về bởi lần gọi next() tiếp theo.
- E previous(): Trả về phần tử liền trước trong danh sách và lùi vị trí con trỏ.
- int previousIndex(): Trả về chỉ số của phần tử sẽ được trả về bởi lần gọi previous() tiếp theo.
- void remove(): Xóa khỏi danh sách phần tử cuối cùng được trả về bởi next() hoặc previous().
- void set(E e): Thay thế phần tử cuối cùng được trả về bởi next() hoặc previous() bằng phần tử được chỉ định.
Chúng ta sẽ khám phá từng phương thức một kèm theo các ví dụ hữu ích ở các phần tiếp theo.
Ví dụ cơ bản về Java ListIterator
Trong phần này, chúng ta sẽ thảo luận một số phương thức của ListIterator kèm theo ví dụ. Trước tiên, chúng ta cần hiểu cách lấy đối tượng iterator này. Làm thế nào để lấy ListIterator?
ListIterator<E> listIterator()
Nó trả về một list iterator duyệt qua các phần tử trong danh sách này. Ví dụ:-
import java.util.*;
public class ListIteratorDemo
{
public static void main(String[] args)
{
List<String> names = new LinkedList<>();
names.add("Rams");
names.add("Posa");
names.add("Chinni");
// Getting ListIterator
ListIterator<String> namesIterator = names.listIterator();
// Traversing elements
while(namesIterator.hasNext()){
System.out.println(namesIterator.next());
}
// Enhanced for loop creates Internal Iterator here.
for(String name: names){
System.out.println(name);
}
}
}
Kết quả:-
Rams
Posa
Chinni
Ví dụ duyệt hai chiều với ListIterator
Trong phần này, chúng ta sẽ khám phá cách các phương thức của ListIterator hoạt động để thực hiện việc duyệt theo chiều tiến và chiều lùi.
import java.util.*;
public class BiDirectinalListIteratorDemo
{
public static void main(String[] args)
{
List<String> names = new LinkedListt<>();
names.add("Rams");
names.add("Posa");
names.add("Chinni");
// Getting ListIterator
ListIterator<String> listIterator = names.listIterator();
// Traversing elements
System.out.println("Forward Direction Iteration:");
while(listIterator.hasNext()){
System.out.println(listIterator.next());
}
// Traversing elements, the iterator is at the end at this point
System.out.println("Backward Direction Iteration:");
while(listIterator.hasPrevious()){
System.out.println(listIterator.previous());
}
}
}
Kết quả:-
Forward Direction Iteration:
Rams
Posa
Chinni
Backward Direction Iteration:
Chinni
Posa
Rams
Các loại Iterator trong Java
Như chúng ta đã biết, Java có bốn con trỏ (cursor): Enumeration, Iterator, ListIterator và Spliterator. Chúng ta có thể phân loại chúng thành hai nhóm chính như dưới đây:
- Uni-Directional Iterators: Đây là các con trỏ chỉ hỗ trợ duyệt theo chiều tiến. Ví dụ: Enumeration, Iterator, v.v. là các Uni-Directional Iterators. Bi-Directional Iterators: Đây là các con trỏ hỗ trợ duyệt theo cả chiều tiến và chiều lùi. Ví dụ: ListIterator là một Bi-Directional Iterator.
Cách Java ListIterator hoạt động bên trong
Như chúng ta đã biết, Java ListIterator hoạt động theo cả hai chiều, nghĩa là nó vừa duyệt theo chiều tiến vừa duyệt theo chiều lùi. Nó là một Iterator hai chiều. Để hỗ trợ chức năng này, nó có hai nhóm phương thức.
- Các phương thức duyệt theo chiều tiến. Chúng ta cần sử dụng các phương thức sau để hỗ trợ duyệt theo chiều tiến:
- hasNext())
- next()
- nextIndex()
- Các phương thức duyệt theo chiều lùi. Chúng ta cần sử dụng các phương thức sau để hỗ trợ duyệt theo chiều lùi:
- hasPrevious()
- previous()
- previousIndex()
Trong bài viết trước, chúng ta đã thảo luận cách Iterator hoạt động bên trong theo chiều tiến trong phần “Java Iterator hoạt động nội bộ như thế nào?”. ListIterator cũng hoạt động theo cách tương tự. Nếu bạn muốn xem lại bài viết trước, vui lòng nhấp vào đây: Java Iterator. Trong phần này, chúng ta sẽ thảo luận cách ListIterator hoạt động theo chiều lùi. Hãy cùng lấy đối tượng LinkedList sau để hiểu rõ chức năng này.
List<String> names = new LinkedList<>();
names.add("E-1");
names.add("E-2");
names.add("E-3");
.
.
.
names.add("E-n");
Bây giờ hãy tạo một đối tượng ListIterator trên LinkedList như được minh họa bên dưới:
ListIterator<String> namesIterator = names.listLterator();
Giả sử ListIterator “namesIterator” trông như dưới đây:
Ở đây con trỏ của ListIterator đang trỏ tới vị trí trước phần tử đầu tiên của danh sách. Bây giờ chúng ta chạy đoạn mã sau trong vòng lặp while.
namesIterator.hasNext();
namesIterator.next();
Khi chúng ta chạy đoạn mã trên trong vòng lặp while, con trỏ của ListIterator sẽ trỏ đến phần tử cuối cùng trong LinkedList.
Sau đó chúng ta có thể chạy đoạn mã sau để bắt đầu duyệt từ cuối danh sách về đầu.
namesIterator.hasPrevious();
namesIterator.previous();
Khi chúng ta chạy đoạn mã trên, con trỏ của ListIterator sẽ trỏ đến phần tử “áp chót” trong danh sách như minh họa ở sơ đồ trên. Thực hiện quá trình này để đưa con trỏ của ListIterator về phần tử đầu tiên của LinkedList.
Sau khi đọc phần tử đầu tiên, nếu chúng ta chạy đoạn mã dưới đây, nó sẽ trả về giá trị “false”.
namesIterator.hasPrevious();
Bản dịch liền mạch (không ngắt dòng): Vì con trỏ của ListIterator đang trỏ tới vị trí trước phần tử đầu tiên của LinkedList nên phương thức hasPrevious() sẽ trả về giá trị false. Sau khi quan sát tất cả các sơ đồ này, chúng ta có thể kết luận rằng Java ListIterator hỗ trợ cả duyệt theo chiều tiến và duyệt theo chiều lùi như minh họa ở các sơ đồ dưới đây. Do đó, nó còn được gọi là Bi-Directional Cursor. ListIterator theo chiều tiến.
ListIterator theo chiều lùi.
Ưu điểm của ListIterator
Không giống như Iterator, ListIterator có các ưu điểm sau:
- Giống như Iterator, nó hỗ trợ các thao tác READ và DELETE.
- Nó cũng hỗ trợ cả các thao tác CREATE và UPDATE.
- Điều đó có nghĩa là nó hỗ trợ đầy đủ các thao tác CRUD: CREATE, READ, UPDATE và DELETE.
- Nó hỗ trợ cả duyệt theo chiều tiến và duyệt theo chiều lùi, nghĩa là nó là một Bi-Directional Cursor trong Java.
- Tên các phương thức đơn giản và dễ sử dụng.
Hạn chế của ListIterator
So với Iterator, Java ListIterator có nhiều ưu điểm. Tuy nhiên, nó vẫn có một số hạn chế sau.
- Nó chỉ là một Iterator dành cho các lớp triển khai List.
- Không giống như Iterator, nó không áp dụng cho toàn bộ Collection API.
- Nó không phải là một Universal Java Cursor.
- So với Spliterator, nó KHÔNG hỗ trợ duyệt song song các phần tử.
- So với Spliterator, nó KHÔNG hỗ trợ hiệu năng tốt hơn khi duyệt một lượng dữ liệu lớn.
Điểm tương đồng giữa Iterator và ListIterator
Trong phần này, chúng ta sẽ thảo luận về những điểm giống nhau giữa hai Cursor trong Java: Iterator và ListIterator.
- Cả hai đều được giới thiệu trong Java 1.2.
- Cả hai đều là Iterator được dùng để duyệt các phần tử của Collection hoặc List.
- Cả hai đều hỗ trợ các thao tác READ và DELETE.
- Cả hai đều hỗ trợ duyệt theo chiều tiến.
- Cả hai đều không phải là các interface Legacy.
Điểm khác biệt giữa Iterator và ListIterator
Trong phần này, chúng ta sẽ thảo luận về sự khác nhau giữa hai Iterator trong Java: Iterator và ListIterator.
Iterator | ListIterator |
---|---|
Được giới thiệu trong Java 1.2. | Được giới thiệu trong Java 1.2. |
Là một Iterator cho toàn bộ Collection API. | Là một Iterator chỉ dành cho các lớp triển khai List. |
Là một Máy lặp phổ quát. | KHÔNG phải là một Máy lặp phổ quát. |
Chỉ hỗ trợ Forward Direction Iteration. | Hỗ trợ cả Forward và Backward Direction Iterations. |
Là một Iterator đơn hướng. | Là một Iterator hai chiều. |
Chỉ hỗ trợ READ và DELETE operations. | Hỗ trợ đầy đủ CRUD operations. |
Có thể lấy Iterator bằng cách sử dụng phương thức iterator(). | Có thể lấy ListIterator object bằng cách sử dụng phương thức listIterator(). |
Đó là tất cả về ListIterator trong Java. Tôi hy vọng những lý thuyết và ví dụ về Java ListIterator này sẽ giúp bạn bắt đầu với lập trình ListIterator.