Интерфейс Map в Java: методы, Map.Entry и примеры - Вопросы

Всего: 5 вопросов

1. 

Чем отличаются методы keySet(), values() и entrySet() интерфейса Map?

keySet() возвращает Set<K> ключей, values() — Collection<V> значений (не Set, потому что значения могут повторяться), а entrySet() — Set<Map.Entry<K, V>> пар, у каждой из которых есть getKey(), getValue() и setValue().

Все три метода возвращают представления (views) самой карты, а не копии: keySet().remove(key) удаляет пару из карты, а entry.setValue() меняет значение прямо в ней. Добавлять через представления нельзя — keySet().add() бросает UnsupportedOperationException, ведь для нового ключа нет значения.

Когда нужны и ключ, и значение, обходят entrySet() или вызывают forEach((k, v) -> ...): обход keySet() с get(key) внутри цикла ищет каждый ключ повторно.

2. 

Как в Map отличить отсутствующий ключ от ключа, которому сопоставлено значение null?

Метод get(key) возвращает null в обоих случаях, поэтому по его результату их не различить. Единственный надёжный способ — containsKey(key): он возвращает true, если ключ есть, даже когда его значение равно null.

getOrDefault(key, defaultValue) тоже не помогает: значение по умолчанию возвращается, только если ключа нет совсем, а для ключа со значением null метод вернёт null. Похожая ловушка у putIfAbsent() — ключ со значением null он считает отсутствующим и перезаписывает.

Если null в значениях возможен, используют Objects.requireNonNullElse(map.get(key), defaultValue) (Java 9) или вовсе не хранят null в карте.

3. 

Как перебрать Map и безопасно удалить из неё элементы во время обхода?

Map перебирают циклом for-each по entrySet(), когда нужны и ключ, и значение, по keySet() или values(), когда нужно что-то одно, или методом forEach((k, v) -> ...) из Java 8.

Вызывать map.remove(key) внутри for-each нельзя: следующий шаг итератора, как правило, бросит ConcurrentModificationException. Безопасные варианты — map.entrySet().removeIf(e -> e.getValue() < 1250), keySet().removeIf(...), values().removeIf(...) или явный итератор с вызовом iterator.remove(). Они удаляют пары через представление, и изменения сразу отражаются в самой карте.

4. 

Как работает метод merge() в Map и как с его помощью посчитать частоту слов?

merge(key, value, remappingFunction) появился в Java 8. Если ключа нет, метод кладёт value; если ключ есть, вызывает функцию со старым и новым значением и сохраняет результат. Если функция вернула null, пара удаляется, а value == null приводит к NullPointerException.

Частота слов считается одной строкой: counts.merge(w, 1, Integer::sum) — для нового слова кладётся 1, для существующего к счётчику прибавляется 1. Вариант counts.put(w, counts.get(w) + 1) падает с NullPointerException на первом же слове: get() возвращает null, а распаковать его в int нельзя.

Для группировки значений в списки используют родственный метод computeIfAbsent(key, k -> new ArrayList<>()).add(value).

5. 

Какие ограничения у карт, созданных через Map.of(), и чем они отличаются от new HashMap<>()?

Map.of(k1, v1, k2, v2, ...) (Java 9) создаёт неизменяемую карту из не более чем 10 пар; для большего числа пар есть Map.ofEntries(Map.entry(k, v), ...), а Map.copyOf(map) (Java 10) делает неизменяемую копию.

У таких карт три ограничения: любые изменения (put(), remove(), clear()) бросают UnsupportedOperationException; повторяющийся ключ — IllegalArgumentException: duplicate key (повторяющиеся значения допустимы); null в ключах и значениях запрещён, даже get(null) бросает NullPointerException. Порядок обхода не определён.

new HashMap<>() изменяема и допускает один null-ключ и null-значения. Изменяемую карту с начальными данными получают обёрткой new HashMap<>(Map.of(...)).

Страница 1 из 1