Java: Как удалить элементы из списка при переборе / добавлении в него - PullRequest
18 голосов
/ 09 марта 2009

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

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

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

Ответы [ 9 ]

17 голосов
/ 09 марта 2009

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

"и" в имени метода - это запах кода.

14 голосов
/ 09 марта 2009

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

Вместо

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

Я использую

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

for(toRemove : thing)
    things.remove(thing)
4 голосов
/ 09 марта 2009

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

3 голосов
/ 09 марта 2009

Вы можете найти эту статью о ConcurrentModificationException содержит несколько советов в этой области.

2 голосов
/ 09 марта 2009

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

1 голос
/ 29 мая 2013

Похоже на firebird84. Но вы можете использовать removeAll (Коллекция 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);
1 голос
/ 19 июля 2011

... удаление файлов, не являющихся 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();
    }
}
1 голос
/ 10 марта 2009

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

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

1 голос
/ 09 марта 2009

Вы должны получить итератор и удалить его. Вы получаете исключение, потому что итераторы отказоустойчивы в Java.

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