Ответ 1
Отключите метод stop() от stopAndRemove(). Затем напишите цикл с явным итератором, сделайте остановку, а затем iterator.remove().
"и" в имени метода является запахом кода.
Этот вопрос является более частным случаем проблемы, описанной (и решенной) в этом вопросе.
У меня есть два метода: stopAndRemove (ServerObject server) и метод close(). Позднее следует закрыть все серверы и удалить их из списка серверов. Список определяется как
List<ServerObject> server.
Я не хочу иметь почти тот же код из stopAndRemove в closeCurrentlyOpen, поэтому я хочу сделать что-то вроде:
public void closeCurrentlyOpen() {
for(ServerObject server : this.servers) {
stopAndRemove(server)
}
}
Это не сработает, так как это вызовет исключение ConcurrentModificationException. Я попытался сделать копию списка
List<ServerObject> copyList = new ArrayList<ServerObject>(this.servers);
и использовать это как список для цикла foreach. Но тогда возможно, что другой поток присоединяет сервер к списку серверов, пока я выполняю повторение с помощью copyList, но closeCurrentlyOpen должен приводить к списку emtpy. Поскольку метод addServerToList синхронизируется с списком серверов, выполните
public void closeCurrentlyOpen() {
synchronized(this.servers) {
for(ServerObject server : this.servers) {
stopAndRemove(server)
}
}
}
решит проблему с модификациями. Но тогда я не могу синхронизировать код в методе stopAndRemove, который необходим, если он вызван напрямую.
Мне кажется, что дизайн этих трех методов, вероятно, нуждается в капитальном ремонте. Идеи кто-нибудь?
Отключите метод stop() от stopAndRemove(). Затем напишите цикл с явным итератором, сделайте остановку, а затем iterator.remove().
"и" в имени метода является запахом кода.
Возможно, это неправильный способ сделать это, но я всегда создаю сборник удаления, содержащий индексы или ссылки на объекты, которые необходимо удалить. Затем я перебираю эту коллекцию и удаляю те индексы/объекты из исходной коллекции. Вероятно, не самый эффективный, но он выполнил свою работу.
Вместо
for(Collection things : thing)
things.remove(thing)
Я использую
Collection toRemove = new LinkedList();
for(things : thing)
toRemove.add(thing);
for(toRemove : thing)
things.remove(thing)
Когда я делал это раньше, я всегда использовал коллекцию LinkedList старой школы, Iterator и Iterator.remove() для удаления текущего элемента.
Вы можете найти в этой статье о ConcurrentModificationException есть рекомендации в этой области.
Восстановите весь код остановки ServerObject от stopAndRemove до частного метода stopServer, а затем выполните удаление отдельно в stopAndRemove и closeCurrentlyOpen. Затем вы можете использовать ListIterator для их удаления (или просто остановить их все в цикле for и очистить список в конце).
Отвечая на заголовок вопроса, а не на конкретные детали данного примера. Фактически, это решение не подходит даже в данной ситуации (рефакторинг подходит, как это было предложено другими).
Однако, похоже, что многие программисты Java не знают CopyOnWriteArrayList (часть JDK с 1.5) и пытаются опрокинуть их собственные решения той же проблемы (список копий перед итерацией).
... удаление файлов, которые не являются XML из списка каталогов...
List<File> files = Arrays.asList(dir.listFiles());
Iterator<File> i = files.iterator();
while (i.hasNext()) {
File file = i.next();
if (!file.getName().endsWith(".xml")) {
i.remove();
}
}
Аналогично firebird84. Но вы можете использовать removeAll (Collection c) api
for(String exitingPermission : existingPermissions){
//remove all permissions for the screen and add the new ones
if(exitingPermission.split("_")[0].equals(screen)){
removePermissions.add(exitingPermission);
}
}
existingPermissions.removeAll(removePermissions);
Вы должны получить итератор и удалить его. Вы получаете исключение, потому что итераторы fail-fast в java.