Hashmap.keySet(), foreach и удалить

Я знаю, что обычно это большой нет-нет для удаления из списка с помощью java "foreach", и что нужно использовать iterator.remove(). Но безопасно ли удалять(), если я зацикливаюсь на HashMap keySet()? Вот так:

for(String key : map.keySet()) {
  Node n = map.get(key).optimize();
  if(n == null) {
   map.remove(key);
  } else {
   map.put(key, n);
  }
}

Ответы

Ответ 1

EDIT:

Я не заметил, что вы на самом деле не добавляли карту - вы просто меняли значение внутри записи. В этом случае решение pstanton (pre-edit 1) почти правильно, но вы должны вызывать setValue в записи, возвращаемой итератором, вместо вызова map.put. (Возможно, что map.put будет работать, но я не верю, что это гарантировано - в то время как документы указывают, что entry.setValue будет работать.)

for (Iterator<Map.Entry<String, Node>> it = map.entrySet().iterator(); 
     it.hasNext();)
{
    Map.Entry<String, Node> entry = it.next();
    Node n = entry.getValue().optimize();
    if(n == null) 
    {
        it.remove();
    }
    else
    {
        entry.setValue(n);
    }
}

(Обидно, что entry не имеет метода remove, иначе вы все равно можете использовать синтаксис расширенного цикла, делая его несколько менее неуклюжим.)

Старый ответ

(Я оставил это здесь для более общего случая, когда вы просто хотите сделать произвольные изменения.)

Нет - вы не должны ни добавлять карту, ни удалять ее напрямую. Набор, возвращаемый HashSet.keySet(), представляет собой представление на клавиши, а не снимок.

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

Один простой вариант - создать новый набор из оригинала:

for (String key : new HashSet<String>(map.keySet())) {
    ...
}

На этом этапе все в порядке, потому что вы не вносите никаких изменений в набор.

EDIT: Да, вы можете определенно удалить элементы с помощью итератора с набором ключей. Из документов для HashMap.keySet():

Набор поддерживает удаление элементов, который удаляет соответствующие отображение с карты через Iterator.remove, Set.remove, removeAll, saveAll и clear операции. Он не поддерживает add или addAll.

Это даже указано в самом интерфейсе Map.


1 Я решил отредактировать свой ответ, а не просто комментировать псантон, поскольку я решил, что дополнительная информация, которую я получил для подобных ситуаций, но была достаточно полезной, чтобы заслужить этот ответ.

Ответ 2

вы должны использовать набор записей:

for(Iterator<Map.Entry<String, Node>> it = map.entrySet().iterator(); it.hasNext();)
{
      Map.Entry<String, Node> entry = it.next();
      Node n = entry.getValue().optimize();
      if(n == null) 
          it.remove();
      else
          entry.setValue(n);
}

Фиксированный код EDIT