1. Bối cảnh về cách thức ra đời của iterator
Bạn đã quen thuộc với HashSet
. Nếu bạn đã thực sự tìm hiểu nó, ngoài việc chỉ đọc một bài học, thì bạn nên đặt câu hỏi này:
Làm cách nào để hiển thị danh sách tất cả các phần tử HashSet trên màn hình? Rốt cuộc, giao diện không có get()
và set()
phương pháp!
Và HashSet
không đơn độc trong giới hạn này. Ngoài HashSet
, còn có nhiều tập hợp khác không cho phép truy xuất các phần tử theo chỉ mục, bởi vì các phần tử không có thứ tự xác định.
Trong những năm qua, các lập trình viên đã phát minh ra rất nhiều cấu trúc dữ liệu phức tạp, chẳng hạn như biểu đồ và cây. Hoặc danh sách của danh sách.
Nhiều vùng chứa thay đổi thứ tự các phần tử của chúng khi các phần tử mới được thêm vào hoặc các phần tử hiện có bị xóa. Ví dụ: một danh sách lưu trữ các phần tử theo một thứ tự cụ thể và khi một phần tử mới được thêm vào, phần tử đó hầu như luôn được chèn vào giữa danh sách.
Và chúng ta cũng gặp tình huống có một thùng chứa lưu trữ các phần tử nhưng không theo bất kỳ thứ tự cố định nào.
Bây giờ, giả sử chúng ta muốn sao chép tất cả các phần tử từ một bộ sưu tập như vậy vào một mảng hoặc danh sách. Chúng ta cần phải có được tất cả các yếu tố. Chúng tôi không quan tâm đến thứ tự mà chúng tôi lặp lại các phần tử — điều quan trọng là không lặp lại các phần tử giống nhau nhiều lần. làm sao chúng ta làm việc đó bây giờ?
2. Iterator cho một bộ sưu tập
Trình lặp được đề xuất như một giải pháp cho vấn đề trên.
Trình lặp là một đối tượng đặc biệt được liên kết với một bộ sưu tập, giúp duyệt qua tất cả các phần tử của bộ sưu tập mà không lặp lại bất kỳ phần tử nào.
Bạn có thể sử dụng đoạn mã sau để nhận một trình vòng lặp cho bất kỳ bộ sưu tập nào:
Iterator<Type> it = name.iterator();
Đâu name
là tên của biến bộ sưu tập, Type
là loại phần tử của bộ sưu tập, iterator()
là một trong các phương thức của bộ sưu tập và it
là tên của biến lặp.
Một đối tượng iterator có 3 phương thức:
Phương pháp | Sự miêu tả |
---|---|
|
Trả về phần tử tiếp theo trong bộ sưu tập |
|
Kiểm tra xem có phần tử nào chưa được duyệt không |
|
Xóa phần tử hiện tại của bộ sưu tập |
Các phương thức này hơi giống với các phương thức của lớp nextInt)
Scanner hasNextInt()
.
Phương next()
thức trả về phần tử tiếp theo của bộ sưu tập mà từ đó chúng ta có trình vòng lặp.
Phương hasNext()
thức kiểm tra xem bộ sưu tập có các phần tử bổ sung mà trình vòng lặp chưa trả về hay không.
Đây là cách hiển thị tất cả các phần tử của a HashSet
:
Mã số | ghi chú |
---|---|
|
Tạo một HashSet đối tượng lưu trữ String các phần tử. Chúng tôi thêm lời chào bằng các ngôn ngữ khác nhau vào set biến. Nhận một đối tượng lặp cho set tập hợp. Miễn là vẫn còn phần tử Lấy phần tử tiếp theo Hiển thị phần tử trên màn hình |
3. For-each
vòng lặp
Nhược điểm chính của trình vòng lặp là mã của bạn trở nên cồng kềnh hơn so với sử dụng for
vòng lặp.
Để so sánh, hãy hiển thị một danh sách bằng cách sử dụng một for
vòng lặp và cũng sử dụng một trình vòng lặp:
Trình lặp | cho vòng lặp |
---|---|
|
|
Vâng, tốt hơn hết là duyệt qua các phần tử của ArrayList
một vòng lặp — mọi thứ trở nên ngắn hơn.
Nhưng những người tạo ra Java lại quyết định đổ một ít đường vào chúng tôi. May mắn cho chúng tôi, đó là đường cú pháp .
Họ đã cung cấp cho Java một loại vòng lặp mới và gọi nó là for-each
vòng lặp. Đây là cách nó trông nói chung:
for(Type name:collection)
Đâu collection
là tên của biến tập hợp, Type
là kiểu của các phần tử trong tập hợp và name
là tên của một biến lấy giá trị tiếp theo từ tập hợp ở mỗi lần lặp của vòng lặp.
Loại vòng lặp này lặp qua tất cả các phần tử của bộ sưu tập bằng cách sử dụng trình vòng lặp ẩn. Đây là cách nó thực sự hoạt động:
Đối với mỗi vòng lặp | Những gì trình biên dịch nhìn thấy: Vòng lặp với một trình vòng lặp |
---|---|
|
|
Khi trình biên dịch gặp một for-each
vòng lặp trong mã của bạn, nó chỉ cần thay thế nó bằng mã ở bên phải: nó thêm một lệnh gọi để nhận một trình vòng lặp cùng với bất kỳ lệnh gọi phương thức bị thiếu nào khác.
Các lập trình viên yêu thích for-each
vòng lặp và hầu như luôn sử dụng nó khi họ cần lặp lại tất cả các phần tử của một tập hợp.
Ngay cả việc lặp lại ArrayList
danh sách bằng for-each
vòng lặp cũng có vẻ ngắn hơn:
Đối với mỗi vòng lặp | cho vòng lặp |
---|---|
|
|
4. Loại bỏ một phần tử trong for-each
vòng lặp
Vòng for-each
lặp có một nhược điểm: nó không thể loại bỏ các phần tử một cách chính xác. Nếu bạn viết mã như thế này, bạn sẽ gặp lỗi.
Mã số | Ghi chú |
---|---|
|
Thao tác xóa sẽ phát sinh lỗi! |
Đây là một mã rất hay và dễ hiểu, nhưng nó sẽ không hoạt động.
Bạn không thể thay đổi một bộ sưu tập trong khi duyệt qua nó bằng một trình vòng lặp.
Có ba cách để vượt qua giới hạn này.
1. Sử dụng một loại vòng lặp khác
When traversing an ArrayList collection
, bạn có thể sử dụng vòng lặp thông thường với i
biến đếm.
Mã số |
---|
|
Tuy nhiên, tùy chọn này không phù hợp với HashSet
và HashMap
bộ sưu tập
2. Sử dụng trình vòng lặp rõ ràng
Bạn có thể sử dụng trình vòng lặp một cách rõ ràng và gọi remove()
phương thức của nó.
Phiên bản hoạt động | Phiên bản không hoạt động |
---|---|
|
|
Lưu ý rằng chúng ta gọi remove()
phương thức trên đối tượng iterator! Trình lặp biết rằng mục đã bị xóa và có thể xử lý tình huống một cách chính xác.
3. Sử dụng một bản sao của bộ sưu tập
Bạn cũng có thể tạo một bản sao của bộ sưu tập, sau đó sử dụng bản sao đó trong một for-each
vòng lặp và xóa các phần tử khỏi bộ sưu tập gốc.
Mã số | Ghi chú |
---|---|
|
Tạo một bản sao của bộ sưu tập cực kỳ dễ dàng Vòng lặp sử dụng trình vòng lặp cho bản sao của bộ sưu tập. Các yếu tố được loại bỏ khỏi list bộ sưu tập. |
Bộ sưu tập được sao chép khá nhanh vì bản thân các phần tử không bị trùng lặp. Thay vào đó, bộ sưu tập mới lưu trữ các tham chiếu đến các phần tử đã tồn tại trong bộ sưu tập cũ.
GO TO FULL VERSION