2012-04-01 148 views
5

我有一個問題與刪除從ArrayList中的賦值 工作時,如果我使用了「正常」的for循環的對象,它的工作原理如下刪除ArrayList對象問題

public void returnBook(String isbn){   
    for (int i = 0; i < booksBorrowed.size(); i++){    
     if (booksBorrowed.get(i).getISBN() == isbn){ 
      booksBorrowed.get(i).returnBook(); 
      booksBorrowed.remove(i);     
     } 
    } 
} 

然而,當我試圖簡化代碼具有增強的for循環,不工作,並顯示java.util.ConcurrentModificationException錯誤:

public void returnBook(String isbn){   
     for (Book book: booksBorrowed){    
      if (book.getISBN() == isbn){ 
       book.returnBook(); 
       booksBorrowed.remove(book);     
      } 
     } 
} 

希望你們能減輕我..

+0

如果你的問題是「爲什麼我得到一個錯誤」,它是因爲您無法從正在迭代的列表中刪除項目。如果相同的ISBN可以在列表中兩次,您的第一個循環可能會有一個錯誤。 – 2012-04-01 03:28:19

回答

7

你的替代方案,以避免ConcurrentModificationException的是:

List<Book> books = new ArrayList<Book>(); 
books.add(new Book(new ISBN("0-201-63361-2"))); 
books.add(new Book(new ISBN("0-201-63361-3"))); 
books.add(new Book(new ISBN("0-201-63361-4"))); 

收集所有要刪除增強的for循環的記錄,並在完成後遍歷,您將刪除所有找到的記錄。

ISBN isbn = new ISBN("0-201-63361-2"); 
List<Book> found = new ArrayList<Book>(); 
for(Book book : books){ 
    if(book.getIsbn().equals(isbn)){ 
     found.add(book); 
    } 
} 
books.removeAll(found); 

或者您可以使用ListIterator,它在迭代過程中支持remove方法。

ListIterator<Book> iter = books.listIterator(); 
while(iter.hasNext()){ 
    if(iter.next().getIsbn().equals(isbn)){ 
     iter.remove(); 
    } 
} 

或者你可以使用第三方庫像LambdaJ,這讓所有的工作,你在幕後>

List<Book> filtered = select(books, 
       having(on(Book.class).getIsbn(), 
         is(new ISBN("0-201-63361-2")))); 
+0

感謝隊友,問題解決了:) – babygau 2012-04-01 04:05:12

+0

你救了我的命。謝謝 – 2017-11-20 11:23:00

4

你真的不應該這樣做,因爲他們最終會造成問題。而是使用ArrayList的迭代器來幫助您遍歷列表,然後僅使用迭代器進行刪除。這將有助於防止有害的併發修改錯誤。

+1

/golfclap使用'惡性' – 2012-04-01 03:28:44

0

當您在Java中使用增強型for-loop時,它使用列表的迭代器遍歷列表。當您使用列表的remove函數移除項目時,這會干擾迭代器的狀態,並且迭代器將拋出ConcurrentModificationException。 使用簡單的for循環,您不會遇到這樣的問題,因爲您只使用列表,並且狀態更改僅在列表本身發生。

+0

小心點亮一下如何使用迭代器刪除Book對象 – babygau 2012-04-01 03:46:49

+0

它真的取決於你的應用程序和你想要的性能。 hashmap和arraylist迭代器的組合將做到這一點。 – amshali 2012-04-01 03:54:51

1

你在你的代碼中的錯誤:

for (int i = 0; i < booksBorrowed.size(); i++){    
    if (booksBorrowed.get(i).getISBN() == isbn){ 
     booksBorrowed.get(i).returnBook(); 
     booksBorrowed.remove(i);     
    } 
} 

它跳過刪除那些後下一個元素。例如。當你刪除'0th'元素時,1st變成0,但是這個代碼不會遍歷它。

這是一個正確的版本:

for (int i = booksBorrowed.size() - 1; i >= 0; i--){    
    if (booksBorrowed.get(i).getISBN() == isbn){ 
     booksBorrowed.get(i).returnBook(); 
     booksBorrowed.remove(i);     
    } 
} 

但是,這是不是最好的方法,因爲它的複雜度爲O(n^2)。

更好的方法是將所有保留的項目添加到另一個集合,然後將它們複製迴帶有截斷大小的原始列表。它的複雜性是O(n)。當然,只有在有很多要刪除的元素時纔會擔心。

P.S.在for-each構造中刪除會破壞迭代器,所以在這種情況下處理列表不是一種有效的方法。

但你可以做到以下幾點:

for (Iterator<String> i = a.iterator(); i.hasNext();) { 
     Book next = i.next(); 
     if (book.getISBN() == isbn){ 
      book.returnBook(); 
      i.remove(i);     
     } 
    } 

同樣,複雜性是在這種情況下爲O(n^2)。

+0

如果他添加超級粗「i--」,第一個循環會起作用。在if語句的底部。 – 2012-04-01 03:47:10

+0

你是什麼意思? – 2012-04-01 03:48:25

+0

在最後一箇中,它是從ArrayList中刪除還是僅從迭代器中刪除? – 2012-04-01 03:53:29

2

所有很好的答案。但我會讓你重新思考它。我的意思是,你真的需要一個ArrayList還是一個HashMap會更好?如果你的對象列表有一個單一關鍵字(ISBN),並用它來獲取每個對象,爲什麼不使用適合你的問題的集合呢?

你woud只做這

public void returnBook(String isbn){   
    Book book = (Book) booksBorrowed.remove(isbn);    
    book.returnBook();  
}