Устранение проблемы объекта ArrayList - PullRequest
5 голосов
/ 01 апреля 2012

У меня возникла проблема с удалением объекта из ArrayList при работе с назначением. Если я использую «обычный» цикл for, он работает следующим образом

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);                
            }
        }
}

Надеюсь, вы, ребята, могли бы меня осветить ..

Ответы [ 5 ]

7 голосов
/ 01 апреля 2012

Ваши альтернативы, чтобы избежать исключения 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, который поддерживает метод удаления во время самой итерации.

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"))));
4 голосов
/ 01 апреля 2012

Вы действительно не должны этого делать, так как в конечном итоге они вызовут проблемы. Вместо этого используйте итератор ArrayList, чтобы помочь вам перебрать список, а затем удалить только с помощью итератора. Это поможет предотвратить пагубные ошибки одновременной модификации.

2 голосов
/ 01 апреля 2012

Все хорошие ответы.Но я бы посоветовал вам переосмыслить это.Я имею в виду, вам действительно нужен ArrayList или HashMap будет лучше?Если в вашем списке объектов есть уникальный ключ (ISBN), и вы используете его для получения каждого объекта, почему бы не использовать коллекцию, предназначенную для вашей проблемы?

Вы будете делать только это

public void returnBook(String isbn){        
     Book book = (Book) booksBorrowed.remove(isbn);            
     book.returnBook();    
}
1 голос
/ 01 апреля 2012

В вашем коде есть ошибка:

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

Она пропускает следующие элементы после удаленных.Например, когда вы удалили «0-й» элемент, 1-й становится 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).Конечно, это важно только в том случае, если нужно удалить много элементов.

Удаление PS в итераторе разрыва для каждой конструкции, поэтому в данном случае это недопустимый способ обработки списка.

Но вы можете сделать следующее:

    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 голосов
/ 01 апреля 2012

Когда вы используете расширенный цикл for в Java, он использует итератор списка для итерации по списку.Когда вы удаляете элемент с помощью функции удаления списка, это будет влиять на состояние итератора, и итератор выдаст исключение ConcurrentModificationException.С простым циклом for у вас нет такой проблемы, потому что вы используете только список, а изменение состояния происходит только в самом списке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...