Java: Cách tốt nhất để lặp qua Bộ sưu tập (tại đây ArrayList)


98

Hôm nay tôi đã rất vui khi viết mã khi nhận được một đoạn mã mà tôi đã sử dụng hàng trăm lần:

Lặp lại qua một Bộ sưu tập (tại đây ArrayList)

Vì một số lý do, tôi thực sự đã xem xét các tùy chọn tự động hoàn thành của Eclipse và nó khiến tôi tự hỏi:

Những trường hợp nào các vòng lặp sau đây tốt hơn để sử dụng hơn các vòng khác?

Vòng lặp chỉ mục mảng cổ điển:

for (int i = 0; i < collection.length; i++) {
  type array_element = collection.get(index);
}

Iterator hasNext () / next ():

for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
  type type = (type) iterator.next();   
}

Và yêu thích của tôi vì nó rất đơn giản để viết:

for (iterable_type iterable_element : collection) {

}

2
Khi nói đến tôi, tôi chủ yếu sử dụng vòng lặp thứ 3.
Harry Joy

1
Đối với cách thứ hai của nó tốt hơn để sử dụng:for (Iterator<type> iterator = collection.iterator(); iterator.hasNext();) { type type = iterator.next(); }
mike jones

4
Giao diện bộ sưu tập không chứa phương thức "get", vì vậy phương thức đầu tiên không phải lúc nào cũng có thể thực hiện được.
ThePerson

Câu trả lời:


104

Cái đầu tiên hữu ích khi bạn cần chỉ mục của phần tử. Điều này về cơ bản tương đương với hai biến thể khác cho ArrayLists, nhưng sẽ thực sự chậm nếu bạn sử dụng a LinkedList.

Cái thứ hai hữu ích khi bạn không cần chỉ mục của phần tử nhưng có thể cần xóa các phần tử khi bạn lặp lại. Nhưng điều này có nhược điểm là IMO hơi dài dòng.

Phiên bản thứ ba cũng là lựa chọn ưu tiên của tôi. Nó ngắn gọn và hoạt động cho tất cả các trường hợp mà bạn không cần bất kỳ chỉ mục nào hoặc trình lặp bên dưới (nghĩa là bạn chỉ đang truy cập các phần tử, không xóa chúng hoặc sửa đổi chúng Collectiontheo bất kỳ cách nào - đó là trường hợp phổ biến nhất).


3
+1 Đánh bại tôi với nó. sẽ đề cập đến LinkedList (không phải tất cả Collection đều rẻ để truy xuất theo chỉ mục).
Scrum Meister

Miễn là một phần tử được yêu cầu chỉ lặp lại, thì việc sử dụng phiên bản thứ 3 luôn tốt, bởi vì việc triển khai đã tồn tại cho tất cả các Bộ sưu tập khác nhau và Sun hoặc bất kỳ triển khai JDK nào đều cố gắng cải thiện hiệu suất hơn là mọi thứ.
Phani

@Phani: Hai biến thể khác cũng hoạt động trên bất kỳ triển khai JDK nào.
MAK

Tôi không nói rằng những thứ đó sẽ không hoạt động, nhưng nếu tình cờ có một số cải tiến hoặc thay đổi hiệu suất nhất định được thực hiện cho việc triển khai các bộ sưu tập, thì nó sẽ tự động áp dụng cho mã của bạn và bạn không cần phải viết để cải thiện hiệu suất là ý tôi.
Phani

1
@Phani: AFAIK, dạng thứ 3 chỉ đơn thuần là đường cú pháp cho dạng thứ 2 (tức là bên dưới, biến thể foreach thực sự sử dụng một trình lặp). Vì vậy, mọi lợi ích về hiệu suất sẽ có sẵn cho cả hai biến thể. Tất nhiên, phiên bản đầu tiên sẽ không nhận được bất kỳ lợi ích nào về hiệu suất (ví dụ: nếu phiên bản Listđược triển khai dưới dạng cây, nó thực sự sẽ chậm hơn).
MAK

36

Tất cả chúng đều có những công dụng riêng:

  1. Nếu bạn có một tệp có thể lặp lại và cần duyệt vô điều kiện đến tất cả chúng:

    cho (iterable_type iterable_element: collection)

  2. Nếu bạn có một tệp có thể lặp lại nhưng cần chuyển qua có điều kiện:

    for (iterator iterator = collection.iterator (); iterator.hasNext ();)

  3. Nếu cấu trúc dữ liệu không triển khai có thể lặp lại:

    for (int i = 0; i <collection.length; i ++)


Bạn có thể làm điều này để duyệt qua phương thức đầu tiên có điều kiện không: if (iterable_element.some_attribute) {// làm gì đó; } else {// không làm gì cả; }. Nếu có thì về cơ bản không có sự khác biệt giữa phương thức thứ nhất và thứ hai, ngoại trừ đường cú pháp.
Thomas Nguyen

1 là cũng giống như thể có điều kiện đi ngang như những người khác sử dụng breakvà / hoặccontinue
arcyqwerty

13

Ngoài ra còn có sử dụng stream () của bộ sưu tập với Java 8

collection.forEach((temp) -> {
            System.out.println(temp);
});

hoặc là

collection.forEach(System.out::println);

Thông tin thêm về luồng Java 8 và các bộ sưu tập cho liên kết dành cho người kỳ diệu


4

Không ai trong số họ là "tốt hơn" so với những người khác. Thứ ba, đối với tôi, dễ đọc hơn, nhưng đối với một người không sử dụng phép đánh răng, nó có thể trông kỳ quặc (họ có thể thích thứ nhất). Cả 3 đều khá rõ ràng đối với bất kỳ ai hiểu Java, vì vậy hãy chọn cái nào khiến bạn cảm thấy tốt hơn về mã.

Mẫu đầu tiên là cơ bản nhất, vì vậy nó là mẫu phổ biến nhất (hoạt động cho các mảng, tất cả các dãy lặp mà tôi có thể nghĩ đến). Đó là sự khác biệt duy nhất tôi có thể nghĩ đến. Trong các trường hợp phức tạp hơn (ví dụ: bạn cần có quyền truy cập vào chỉ mục hiện tại hoặc bạn cần lọc danh sách), trường hợp thứ nhất và thứ hai có thể có ý nghĩa hơn, tương ứng. Đối với trường hợp đơn giản (đối tượng có thể lặp lại, không có yêu cầu đặc biệt), thứ ba có vẻ là sạch nhất.


2

Tùy chọn đầu tiên là hiệu suất tốt hơn khôn ngoan (Khi ArrayList triển khai giao diện RandomAccess). Theo tài liệu java, một triển khai Danh sách nên triển khai giao diện RandomAccess nếu, đối với các trường hợp điển hình của lớp, vòng lặp này:

 for (int i=0, n=list.size(); i < n; i++)
     list.get(i);

chạy nhanh hơn vòng lặp này:

 for (Iterator i=list.iterator(); i.hasNext(); )
     i.next();

Tôi hy vọng nó sẽ giúp. Tùy chọn đầu tiên sẽ chậm đối với danh sách truy cập tuần tự.


1

Đây là một ví dụ

Query query = em.createQuery("from Student");
             java.util.List list = query.getResultList();
             for (int i = 0; i < list.size(); i++) 
             {

                 student = (Student) list.get(i);
                 System.out.println(student.id  + "  " + student.age + " " + student.name + " " + student.prenom);

             }
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.