2013-02-26 30 views
11

Tôi có dưới lớp hai javaDanh sách ném ConcurrentModificationException nhưng thiết lập không ném ConcurrentModificationException?

import java.util.*; 

public class ArrayListTest032 { 
    public static void main(String[] ar) { 
     List<String> list = new ArrayList<String>(); 
     list.add("core java"); 
     list.add("php"); 
     list.add("j2ee"); 
     list.add("struts"); 
     list.add("hibernate"); 

     Iterator<String> itr = list.iterator(); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 
     list.remove("php"); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 

    } 
} 

Khi tôi chạy trên mã tôi nhận được bên dưới đầu ra.

core java 
php 
j2ee 
struts 
hibernate 

Exception in thread "main" java.util.ConcurrentModificationException 
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) 
    at java.util.AbstractList$Itr.next(AbstractList.java:343) 
    at ArrayListTest032.main(ArrayListTest032.java:20) 

Điều gì được mong đợi khi tôi sửa đổi danh sách trong khi lặp lại. Nhưng ở bên dưới lớp logic tương tự java được thực hiện bởi nhóm gia đình.

import java.util.*; 

public class HashSetTest021 { 
    public static void main(String[] ar) { 
     Set<String> set = new HashSet<String>(); 
     set.add("core java"); 
     set.add("php"); 
     set.add("j2ee"); 
     set.add("struts"); 
     set.add("hibernate"); 

     Iterator<String> itr = set.iterator(); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 
     set.remove("php"); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 

    } 
} 

Và đặt ra là.

hibernate 
core java 
j2ee 
php 
struts 

Không có ConcurrentModificationException bất kỳ.

Tôi chỉ muốn biết tại sao cùng một đoạn mã ném ConcurrentModificationException trong trường hợp list gia đình, nhưng không có bất kỳ ConcurrentModificationException trong trường hợp set gia đình

Trả lời

4

Đây là sự khác biệt trong việc triển khai: trình vòng lặp được trả về bởi danh sách mảng phát hiện các sửa đổi đồng thời ngay cả khi nó được định vị ở cuối, vì nó kiểm tra độ dài; Ngược lại, các trình vòng lặp của HashSet, TreeSetLinkedList không phát hiện tình trạng này, bởi vì chúng kiểm tra được định vị ở cuối trước khi kiểm tra sửa đổi đồng thời. Tài liệu cho phép các trình vòng lặp không ném vào các sửa đổi đồng thời, vì vậy cả hai phương pháp đều hợp lệ.

+0

+1 cho demo fiddle –

+0

nit: trình lặp ArrayList thực sự kiểm tra số lần sửa đổi, không phải độ dài mỗi lần, vì vậy nếu bạn thêm thì ngay lập tức xóa phần tử, để nguyên độ dài, bạn vẫn sẽ nhận được một ConcurrentModificationException sau đó. –

1
public static void main(String[] ar) { 
      List<String> list = new ArrayList<String>(); 
      list.add("core java"); 
      list.add("php"); 
      list.add("j2ee"); 
      list.add("struts"); 
      list.add("hibernate"); 

      Iterator<String> itr = list.iterator(); 

      while (itr.hasNext()) { 
       System.out.println(itr.next()); 
      } 
      list.remove("php"); 

      /* while (itr.hasNext()) { 
       System.out.println(itr.next()); 
      }*/ 

     } 

problem in itr object.it holds the list object reference 
+0

thì tại sao đặt không ném ngoại lệ. bạn có thể kiểm tra cả hai lớp –

+0

Từ dấu vết ngăn xếp đầu ra, rõ ràng là ngoại lệ sẽ đến khi chúng ta gọi hàm lặp(). Nếu bạn đang tự hỏi làm thế nào Iterator kiểm tra sửa đổi, việc thực hiện của nó có mặt trong lớp AbstractList nơi một biến int modCount được định nghĩa để cung cấp số lần kích thước danh sách đã được thay đổi. – Biswajit

5

Đây là một loại ' hành vi ngược dòng ', không giống như các trình lặp, một khi được duyệt hoàn toàn, không thể tái sử dụng được, còn gọi là phương thức hasNext của chúng ta sẽ trả về false khi bạn đến cuối danh sách.

Trong trường hợp này, mặc dù các iterator được trả về bởi ArrayList.iterator là một lớp thực hiện nội bộ, với mã cho hasNext như sau:

public boolean hasNext() { 
    return cursor != size; 
} 

Vì vậy, khi bạn gọi hasNext trong vòng lặp thứ hai của bạn, nó chỉ ra (sai) mà có nhiều mục hơn để lặp lại, bởi vì bạn đã thực hiện một thao tác đã thay đổi kích thước của danh sách, sau lần lặp đầu tiên. Về mặt ngữ nghĩa, bạn không thể tiếp tục lặp qua các mục trong danh sách sau khi bạn đạt đến kết thúc của nó, nhưng do chi tiết triển khai này, nó cho phép bạn tiếp tục với vòng lặp thứ hai. Tất nhiên, tại thời điểm đó, bạn nhận được một ngoại lệ sửa đổi đồng thời vì sự thay đổi bạn đã thực hiện trong danh sách sao lưu.

Mặt khác, các iterator được sử dụng bởi bộ băm của bạn có nó hasNext thực hiện như sau:

public final boolean hasNext() { 
    return next != null; 
} 

thực hiện này không xảy ra là như 'dễ bị tổn thương' để điều chỉnh thực hiện cho các hash thiết lập sau khi một lần lặp đã được hoàn thành và phương thức hasNext hoạt động tốt hơn.

+0

Ok và trong trường hợp thiết lập gia đình? –

+0

@Real - các chi tiết bổ sung để triển khai cài đặt băm. – Perception

2

Bắt đầu bằng cách đọc JavaDoc cho Iterator. Nó có đề cập đến ConcurrentModificationException ở bất kỳ đâu không?

Bây giờ, đọc javadoc cho ConcurrentModificationException, và lưu ý những điều sau (nhấn mạnh thêm):

ngoại lệ này thể được ném bằng các phương pháp đã được phát hiện đồng thời sửa đổi của một đối tượng khi sửa đổi như vậy không phải là được phép.

Bây giờ hãy xem kỹ mã của bạn. Vòng lặp while của bạn lặp qua tất cả các phần tử của bộ sưu tập (mặc dù đầu ra của ví dụ đầu tiên của bạn không biểu thị điều này, cho biết rằng bạn đã chỉnh sửa đầu ra hoặc đây không phải là mã thực của bạn). Vào thời điểm bạn xóa phần tử, không có thêm mục nào để lặp lại, vì vậy vòng lặp thứ hai sẽ luôn luôn thoát ngay lập tức.

Vì vậy, kết luận là những người thực hiện danh sách iterator có chọn rằng để ném ngoại lệ mà ngay cả khi không có nhiều yếu tố để lặp, trong khi những người thực hiện của tập iterator có chọn không. Cả hai trường hợp đều hoàn toàn chấp nhận được thông số kỹ thuật.

+0

Tôi đã thêm cùng một lớp trong mã trên, bạn có thể chạy mã và kiểm tra đầu ra cho mình. –

+0

@Real - vâng, tôi đã chạy nó và đầu ra cho thấy "hibernate", mà bài đăng của bạn không có. Nhưng thực sự, thay vì nhận được quần short của bạn trong một twist về một bình luận parenthetical, bạn thực sự nên suy nghĩ về * nội dung * của câu trả lời của tôi. – parsifal

+0

Bất kỳ ý tưởng nào tại sao thiết kế này trong đó danh sách ném và đặt không được ưa thích? – djechlin

1

Hashset có thể ném một ConcurrentModificationException nếu bạn làm bất cứ điều gì với tập hợp ngoại trừ thông qua trình lặp. Tuy nhiên, có rất nhiều phỏng đoán xung quanh hành vi thất bại nhanh của trình lặp với mục tiêu hoàn thành việc lặp lại nếu có thể. Các JavaDocs có vẻ khá rõ ràng về hành vi của nó.

0

Trong trường hợp danh sách khi chúng tôi duyệt qua vòng đầu tiên Iterator itr = set.iterator();

while (itr.hasNext()) { 
     System.out.println(itr.next()); 
    } 

Giá trị con trỏ và kích thước sẽ trở thành same.Cursor chứa giá trị cho tổng không có các yếu tố đi qua và bên hashNext() phương pháp cho danh sách traversal chứa mã như:

public boolean hasNext() { 
      return cursor != size; 
     } 

Vì vậy, sau khi lần đầu tiên trong khi vòng lặp Kích thước con trỏ ==.Nhưng sau khi loại bỏ phần tử khỏi kích thước danh sách trở thành (originalSize-1) .Vì vậy, trong vòng lặp tiếp theo, nó đi vào bên trong và bên trong phương thức itr.next() nó kiểm tra sửa đổi và ném ConcurrentModificationException.

Trong trường hợp Đặt nó kiểm tra tiếp theo! = Null cho mỗi cuộc gọi itr.hasnext(). Và sau khi vượt qua vòng lặp đầu tiên trong khi tiếp theo trở thành null.Thông số di chuyển từ bộ không ảnh hưởng đến giá trị tiếp theo là null và itr.hasNext sẽ return next == null là true và do đó nó không đi bên trong trong khi vòng lặp để kiểm tra modcount modify.Và do đó nó không ném ConcurrentModification Exception.

Các vấn đề liên quan