При обработке записей через представление entrySet изменение объекта Map.Entry меняет исходную карту. Какой контракт представления объясняет это поведение?
Map.entrySet() возвращает не независимую копию, а представление, связанное с исходной картой. В изменяемых реализациях, таких как HashMap, вызов Map.Entry.setValue изменяет значение соответствующего отображения в самой карте.
В Java Collections Framework представления коллекций позволяют работать с частью структуры данных без создания полной копии. Такой подход уменьшает расход памяти и поддерживает согласованность: изменение поддерживаемой операции через представление отражается в исходной коллекции.
Разработчик может ошибочно принять объект Map.Entry за независимую пару ключ-значение. Тогда изменение записи неожиданно изменит карту, а попытка сохранить такие объекты для последующей работы может привести к неверным предположениям о времени жизни и независимости данных.
Особенно важно различать изменение значения через setValue и изменение структуры карты. Добавление или удаление записей во время обычного обхода карты может нарушить контракт итератора и привести к ConcurrentModificationException.
entrySet() предоставляет множество записей карты, где каждая запись логически соответствует текущему отображению ключа в значение. Для изменяемой карты операция setValue у записи обычно делегируется внутреннему обновлению значения по этому ключу.
Минимальный пример:
Здесь не создаётся новая карта: entrySet связано с scores, поэтому значение становится равным 11 в исходной карте. Это отличается от копирования записей в отдельный список или другую карту.
Поддержка setValue является опциональной операцией контракта Map.Entry. Для неизменяемых карт и некоторых специальных представлений попытка изменить значение может завершиться UnsupportedOperationException. Поэтому переносить поведение HashMap без проверки на любую реализацию Map нельзя.
Через entrySet также можно удалять отображения, если это поддерживает конкретная карта и её итератор. Однако структурные изменения во время обхода следует выполнять через Iterator.remove, а не произвольно через саму карту.
Сервис хранит оценки пользователей в HashMap и должен увеличить каждую оценку на один балл. Возможны три варианта.
Создать новую карту — безопасно изолирует результат, но требует дополнительной памяти и копирования всех записей. Вызвать put для каждого ключа — явно изменяет карту, но при обходе нужно учитывать ограничения структурных изменений и не добавлять или удалять записи без предусмотренного механизма.
Использовать entrySet и setValue — наиболее прямое решение, если требуется только заменить значения существующих записей. Оно не создаёт копию и не меняет структуру карты, поэтому подходит для такого преобразования; результатом становится обновлённая исходная карта с минимальными накладными расходами.
entrySet можно изменять через setValue?Нет. Map.Entry.setValue — опциональная операция. У HashMap она поддерживается, но у неизменяемых карт и некоторых специальных реализаций может выбрасываться UnsupportedOperationException. Код, рассчитанный на произвольный Map, должен учитывать это ограничение.
entrySet полноценной независимой парой данных?Нет. В обычном случае это объект, представляющий отображение карты, а не снимок. Его значение может отражать состояние карты, а setValue может менять карту. После удаления записи из карты или завершения некоторых специальных итераций нельзя безоговорочно рассчитывать на независимость и актуальность объекта Map.Entry.
entrySet удалять записи вызовом map.remove?Так делать небезопасно для обычных fail-fast итераторов. Удаление изменяет структуру карты и может привести к ConcurrentModificationException. Для удаления текущей записи следует использовать Iterator.remove, если операция поддерживается; если нужны массовые изменения, лучше применить предусмотренный метод коллекции или сформировать отдельный результат.