2010-07-06 50 views
63

Tôi có đoạn mã sau:ConcurrentModificationException cho ArrayList

private String toString(List<DrugStrength> aDrugStrengthList) { 
    StringBuilder str = new StringBuilder(); 
     for (DrugStrength aDrugStrength : aDrugStrengthList) { 
      if (!aDrugStrength.isValidDrugDescription()) { 
       aDrugStrengthList.remove(aDrugStrength); 
      } 
     } 
     str.append(aDrugStrengthList); 
     if (str.indexOf("]") != -1) { 
      str.insert(str.lastIndexOf("]"), "\n   "); 
     } 
    return str.toString(); 
} 

Khi tôi cố gắng chạy nó, tôi nhận được ConcurrentModificationException, bất cứ ai có thể giải thích lý do tại sao nó xảy ra, ngay cả khi đang chạy trong cùng một chủ đề? Và làm thế nào tôi có thể tránh nó?

+3

[Bạn nên ngừng lo lắng và tình yêu giá trị nhân bản.] (Http://blog.stackoverflow.com/2010/11/dr-strangedupe-or-how- i-learning-to-stop-lo lắng-và-tình yêu-trùng lặp /). – Will

+1

Giải thích về ngoại lệ này là trình lặp của ArrayList là một trình lặp không nhanh; tức là nó sẽ thất bại (ném ngoại lệ) khi nó phát hiện rằng bộ sưu tập của nó trong thời gian trung bình đã được sửa đổi. So với các trình lặp không an toàn không loại trừ các ngoại lệ sửa đổi đồng thời (ví dụ: trên các bộ sưu tập ConcurrentHashMap và CopyOnWriteArrayList) –

Trả lời

136

Bạn không thể xóa khỏi danh sách nếu bạn đang duyệt qua vòng lặp "cho mỗi". Bạn có thể sử dụng Iterator. Thay thế:

for (DrugStrength aDrugStrength : aDrugStrengthList) { 
    if (!aDrugStrength.isValidDrugDescription()) { 
     aDrugStrengthList.remove(aDrugStrength); 
    } 
} 

Với:

for (Iterator<DrugStrength> it = aDrugStrengthList.iterator(); it.hasNext();) { 
    DrugStrength aDrugStrength = it.next(); 
    if (!aDrugStrength.isValidDrugDescription()) { 
     it.remove(); 
    } 
} 
+0

cú pháp foreach của java thực sự sử dụng Iterator, một số IDE sẽ báo cáo giải pháp này và đề xuất thay thế bằng foreach (cho (MyListener listener: MyListenerList)) –

+0

@HugoGresse Có, nhưng đây là hướng ngược lại. Iterator cho thấy 'remove' an toàn cho việc lặp lại của nó, cái gì đó cho phép" mất ". –

+2

không biết rằng cảm ơn bạn @KonradGarus –

5

Trong khi lặp qua vòng lặp, bạn đang cố gắng thay đổi giá trị Danh sách trong thao tác remove(). Điều này sẽ dẫn đến ConcurrentModificationException.

Thực hiện theo các mã dưới đây, mà sẽ đạt được những gì bạn muốn nhưng sẽ không ném bất kỳ trường hợp ngoại lệ

private String toString(List aDrugStrengthList) { 
     StringBuilder str = new StringBuilder(); 
    List removalList = new ArrayList(); 
    for (DrugStrength aDrugStrength : aDrugStrengthList) { 
     if (!aDrugStrength.isValidDrugDescription()) { 
      removalList.add(aDrugStrength); 
     } 
    } 
    aDrugStrengthList.removeAll(removalList); 
    str.append(aDrugStrengthList); 
    if (str.indexOf("]") != -1) { 
     str.insert(str.lastIndexOf("]"), "\n   "); 
    } 
    return str.toString(); 
} 
+0

Tại sao lại là downvote? – bragboy

+2

'aDrugStrengthList.removeAll (removeList)' –

+0

@TimBender - cảm ơn bạn đã chỉnh sửa câu trả lời. – bragboy

22

Giống như câu trả lời khác nói, bạn không thể xóa một mục từ một bộ sưu tập bạn đang iterating trên . Bạn có thể giải quyết vấn đề này bằng cách sử dụng một cách rõ ràng Iterator và xóa mục đó ở đó.

Iterator<Item> iter = list.iterator(); 
while(iter.hasNext()) { 
    Item blah = iter.next(); 
    if(...) { 
    iter.remove(); // Removes the 'current' item 
    } 
} 
5

có nên có một implemention đồng thời Danh sách giao diện hỗ trợ hoạt động như vậy.

thử java.util.concurrent.CopyOnWriteArrayList.class

+0

Tôi đã gặp sự cố tương tự với HashMap, được sửa với một triển khai khác của giao diện Bản đồ. Bạn nên tự mình kiểm tra. Tôi không biết chi tiết về CopyOnWriteArrayList – idiotgenius

12

Tôi thích một thứ tự ngược vòng lặp for như:

int size = list.size(); 
for (int i = size - 1; i >= 0; i--) { 
    if(remove){ 
     list.remove(i); 
    } 
} 

vì nó không đòi hỏi học bất kỳ cấu trúc dữ liệu mới hoặc các lớp học.

+0

WOW THANK YOU. Tôi không biết mẹo này. –