2013-04-13 34 views
21

Chúng ta đều biết rằng cách an toàn nhất "và có lẽ chỉ an toàn" để loại bỏ một đối tượng khỏi bộ sưu tập trong khi lặp lại nó, trước tiên là lấy Iterator, thực hiện vòng lặp và xóa khi cần;Cách thức loại bỏ của Iterator thực sự loại bỏ một đối tượng

Iterator iter=Collection.iterator(); 
while(iter.hasNext()){ 
    Object o=iter.next() 
    if(o.equals(what i'm looking for)){ 
     iter.remove(); 
    } 
} 

Những gì tôi muốn hiểu, và tiếc là đã không tìm thấy một lời giải thích kỹ thuật sâu sắc về, là làm thế nào loại bỏ này được thực hiện,
Nếu:

for(Object o:myCollection().getObjects()){ 
    if(o.equals(what i'm looking for)){ 
     myCollection.remove(o); 
    } 
} 

sẽ ném một ConcurrentModificationException, những gì có "trong thuật ngữ kỹ thuật" Iterator.remove() làm gì? Liệu nó loại bỏ các đối tượng, phá vỡ vòng lặp và khởi động lại vòng lặp?

tôi thấy trong các tài liệu chính thức:

"Loại bỏ các yếu tố hiện Ném IllegalStateException nếu một nỗ lực được thực hiện để gọi remove() mà không được đi trước bởi một cuộc gọi đến tiếp theo().."

Phần "xóa phần tử hiện tại", làm cho tôi nghĩ về tình huống tương tự xảy ra trong vòng lặp "thông thường" = (thực hiện kiểm tra bình đẳng và xóa nếu cần), nhưng tại sao vòng lặp Iterator ConcurrentModification- an toàn?

+0

bạn có thể xem cho chính mình: https://gist.github.com/kibotu/e480bd7505615a7311a6 –

Trả lời

14

Làm thế nào chính xác Iterator loại bỏ các yếu tố phụ thuộc vào việc thực hiện của nó, có thể khác nhau cho các bộ sưu tập khác nhau. Chắc chắn nó không phá vỡ các vòng lặp bạn đang ở trong tôi vừa nhìn như thế nào ArrayList iterator được thực hiện và đây là đoạn code:.

public void remove() { 
    if (lastRet < 0) 
     throw new IllegalStateException(); 
    checkForComodification(); 

    try { 
     ArrayList.this.remove(lastRet); 
     cursor = lastRet; 
     lastRet = -1; 
     expectedModCount = modCount; 
    } catch (IndexOutOfBoundsException ex) { 
     throw new ConcurrentModificationException(); 
    } 
} 

Vì vậy, nó kiểm tra các thay đổi đồng thời, loại bỏ yếu tố sử dụng ArrayList công loại bỏ phương pháp, và gia số truy cập của các sửa đổi danh sách để ConcurrentModificationException sẽ không được ném vào lần lặp tiếp theo.

+1

'lastRet' là gì? – m0skit0

+1

Chỉ mục của phần tử cuối được trả về bởi trình lặp. Nó được đặt thành -1 vì phần tử này vừa bị xóa khỏi danh sách. –

+0

Java của tôi hơi bị gỉ - nhưng 'ArrayList.this.remove (lastRet) 'là gì? Tại sao nó cần phải viết 'ArrayList.this'? Nó là một lớp bên trong hay cái gì đó? –

17

Lý do tại sao bạn không thể sửa đổi danh sách trong khi lặp qua nó là bởi vì trình vòng lặp phải biết phải trả về cái gì cho hasNext() và tiếp theo().

thế nào điều này được thực hiện là triển khai thực hiện cụ thể, nhưng bạn có thể có một cái nhìn vào mã nguồn của ArrayList/AbstractList/LinkedList, vv

Cũng lưu ý rằng trong một số trường hợp, bạn có thể sử dụng một số mã như thế này như một sự thay thế :

List<Foo> copyList = new ArrayList<>(origList); 
for (Foo foo : copyList){ 
    if (condition){ 
    origList.remove(foo); 
    } 
} 

Nhưng mã này có thể sẽ chạy chậm hơn một chút vì phải thu thập bộ sưu tập (chỉ bản sao nông) và phần tử cần xóa.

Cũng lưu ý rằng nếu bạn đang sử dụng iterator trực tiếp thì bạn nên sử dụng một vòng lặp for thay vì vòng lặp while như này giới hạn phạm vi của biến:

for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){ 
... 
} 
Các vấn đề liên quan