Подтвердить что ты не робот

Java: как удалить элементы из списка при повторении и добавлении к нему

Этот вопрос является более частным случаем проблемы, описанной (и решенной) в этом вопросе.

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

Мне кажется, что дизайн этих трех методов, вероятно, нуждается в капитальном ремонте. Идеи кто-нибудь?

4b9b3361

Ответ 1

Отключите метод stop() от stopAndRemove(). Затем напишите цикл с явным итератором, сделайте остановку, а затем iterator.remove().

"и" в имени метода является запахом кода.

Ответ 2

Возможно, это неправильный способ сделать это, но я всегда создаю сборник удаления, содержащий индексы или ссылки на объекты, которые необходимо удалить. Затем я перебираю эту коллекцию и удаляю те индексы/объекты из исходной коллекции. Вероятно, не самый эффективный, но он выполнил свою работу.

Вместо

for(Collection things : thing)  
    things.remove(thing)

Я использую

Collection toRemove = new LinkedList();
for(things : thing)
    toRemove.add(thing);

for(toRemove : thing)
    things.remove(thing)

Ответ 3

Когда я делал это раньше, я всегда использовал коллекцию LinkedList старой школы, Iterator и Iterator.remove() для удаления текущего элемента.

Ответ 5

Восстановите весь код остановки ServerObject от stopAndRemove до частного метода stopServer, а затем выполните удаление отдельно в stopAndRemove и closeCurrentlyOpen. Затем вы можете использовать ListIterator для их удаления (или просто остановить их все в цикле for и очистить список в конце).

Ответ 6

Отвечая на заголовок вопроса, а не на конкретные детали данного примера. Фактически, это решение не подходит даже в данной ситуации (рефакторинг подходит, как это было предложено другими). ​​

Однако, похоже, что многие программисты Java не знают CopyOnWriteArrayList (часть JDK с 1.5) и пытаются опрокинуть их собственные решения той же проблемы (список копий перед итерацией).

Ответ 7

... удаление файлов, которые не являются 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();
    }
}

Ответ 8

Аналогично 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);

Ответ 9

Вы должны получить итератор и удалить его. Вы получаете исключение, потому что итераторы fail-fast в java.