Какие основные операции на карте разрешены при ее итерации? - PullRequest
11 голосов
/ 29 января 2009

Скажем, я перебираю карту в Java ... Мне неясно, что я могу сделать с этой картой, пока я перебираю ее. Наверное, меня больше всего смущает это предупреждение в Javadoc для метода удаления интерфейса Iterator:

[...] Поведение итератора не определено, если базовая коллекция изменена во время выполнения итерации любым способом, кроме вызова этого метода.

Я точно знаю, что могу вызвать метод удаления без каких-либо проблем. Но, перебирая коллекцию Map, могу ли я:

  1. Изменить значение, связанное с ключом, с помощью метода put класса Map (положить с существующим ключом)?

  2. Добавить новую запись с помощью метода put класса Map (положить с новым ключом)?

  3. Удалить запись с помощью метода удаления класса Map?

Я предполагаю, что я могу, вероятно, безопасно сделать # 1 (положить в существующий ключ), но не безопасно сделать # 2 или # 3.

Заранее спасибо за любые разъяснения по этому вопросу.

Ответы [ 4 ]

13 голосов
/ 29 января 2009

Вы можете использовать Iterator.remove(), а при использовании итератора entrySet (из Map.Entry) вы можете использовать Map.Entry.setValue(). Все остальное и все ставки отключены - вы не должны менять карту напрямую, а некоторые карты не разрешать ни один, ни оба вышеупомянутых метода.

В частности, ваши (1), (2) и (3) не разрешены.

Вы могли бы избежать установки значения существующего ключа через объект Map, но документация Set.iterator() специально исключает это, и это будет зависеть от реализации:

Если карта модифицируется во время выполнения итерации по набору (кроме как через собственную операцию удаления итератора или через операцию setValue для записи карты, возвращаемой итератором), результаты итерации не определены . (выделение добавлено)

2 голосов
/ 29 января 2009

Глобального ответа нет. Интерфейс карты предоставил выбор пользователям. К сожалению, я думаю, что все реализации в jdk используют реализацию fail-fast (вот определение fail-fast, как указано в HashMap Javadoc ):

Итераторы, возвращаемые всем этим классы "методы представления коллекции" безотказный: если карта структурно изменено в любое время после итератор создается, кроме как через собственное удаление итератора метод, итератор бросит ConcurrentModificationException. Таким образом, перед лицом одновременного модификация, итератор терпит неудачу быстро и чисто, а не риск произвольный, недетерминированный поведение в неопределенное время в будущее.

2 голосов
/ 29 января 2009

Если вы посмотрите на класс HashMap, вы увидите поле с именем 'modCount'. Так карта узнает, когда она была изменена во время итерации. Любой метод, который увеличивает значение modCount при выполнении итерации, заставит его вызвать исключение ConcurrentModificationException.

Тем не менее, вы МОЖЕТЕ поместить значение в карту, если ключ уже существует, эффективно обновляя запись новым значением:

 Map<String, Object> test = new HashMap<String, Object>();
 test.put("test", 1);

 for(String key : test.keySet())
 {
     test.put(key, 2); // this works!
 }

 System.out.println(test); // will print "test->2"

Когда вы спрашиваете, можете ли вы выполнить эти операции «безопасно», вам не нужно слишком беспокоиться, потому что HashMap разработан для того, чтобы вызвать это исключение ConcurrentModificationException, как только оно столкнется с такой проблемой. Эти операции потерпят неудачу быстро; они не оставят карту в плохом состоянии.

0 голосов
/ 29 января 2009

В общем, если вы хотите изменить карту во время итерации, вы должны использовать один из методов итератора. Я на самом деле не проверял, будет ли № 1 работать, но другие точно не будут.

...