W3docs

Как перебрать HashMap в Java

Перебор записей Java HashMap с помощью entrySet, keySet, values, forEach и потоков.

HashMap хранит пары ключ-значение, и рано или поздно вам понадобится пройтись по ним — чтобы вывести отчёт, подсчитать сумму значений или отфильтровать записи. Java предлагает несколько идиоматичных способов сделать это, каждый из которых подходит для немного разных задач: нужны ли вам ключи, значения или и то и другое? В этой главе рассматриваются наиболее распространённые подходы — entrySet(), keySet(), values(), forEach и удаляющий Iterator — и объясняется, когда к ним следует прибегать.

Эти приёмы применимы к любой реализации Map, включая LinkedHashMap и TreeMap, поскольку все они разделяют единый API итерации.

Перебор записей с помощью entrySet()

Когда вам нужны и ключ, и значение, entrySet() является наиболее эффективным выбором. Он возвращает представление объектов Map.Entry, и за один проход вы получаете каждую пару без дополнительного поиска:

Map<String, Integer> stock = new HashMap<>();
for (Map.Entry<String, Integer> e : stock.entrySet()) {
    System.out.println(e.getKey() + " -> " + e.getValue());
}

Это рекомендуемый подход по умолчанию. Использование keySet() с последующим вызовом map.get(key) внутри цикла выполняет избыточный хэш-поиск для каждого элемента; entrySet() полностью избавляет от этого.

Итерация только ключей или только значений

Если вас интересует лишь одна сторона каждой пары, запросите только это представление. keySet() возвращает ключи, а values() возвращает значения:

for (String key : stock.keySet()) {
    System.out.println("key: " + key);
}
for (int qty : stock.values()) {
    System.out.println("qty: " + qty);
}

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

Метод forEach

Начиная с Java 8, у Map есть метод forEach, принимающий BiConsumer и передающий ключ и значение в качестве параметров лямбды. Он лаконичен и хорошо читается для простых побочных эффектов:

stock.forEach((key, value) -> System.out.println(key + "=" + value));

Внутри лямбды нет break или continue, поэтому для раннего выхода или сложного управления потоком классический цикл for по-прежнему нагляднее.

Безопасное удаление с помощью Iterator

Структурное изменение карты во время работы цикла for-each вызывает ConcurrentModificationException. Чтобы удалять записи в процессе обхода, используйте явный Iterator и вызывайте его метод remove():

Iterator<Map.Entry<String, Integer>> it = stock.entrySet().iterator();
while (it.hasNext()) {
    if (it.next().getValue() < 10) {
        it.remove();
    }
}

Современная альтернатива — stock.entrySet().removeIf(e -> e.getValue() < 10), которая выражает тот же фильтр в одну строку.

ПодходДаёт вамЛучше всего для
entrySet()ключ + значениевариант по умолчанию; чтение обоих
keySet()только ключиработа с ключами
values()только значениясуммы, сканирование значений
forEachключ + значение (лямбда)лаконичные побочные эффекты
Iteratorключ + значениеудаление в процессе обхода
java— editable, runs on the server

Что можно извлечь из запуска:

  • Цикл entrySet() считывает каждый ключ и значение за один проход и накапливает Total stock: 39, суммируя 12 + 7 + 20.
  • keySet() выводит только ключи (apple, banana, cherry), тогда как values() — только числа, демонстрируя, что каждое представление открывает одну сторону пары.
  • Лямбда forEach выдаёт те же строки key=value, что и ручной цикл, подтверждая, что это лаконичный эквивалент для простой итерации.
  • Был использован LinkedHashMap, чтобы вывод сохранял порядок вставки — обычный HashMap не даёт гарантий порядка, поэтому его строки могут появляться в любой последовательности.
  • Вызов Iterator.remove() удаляет banana (значение 7, меньше 10) и оставляет {apple=12, cherry=20}, демонстрируя безопасное удаление внутри цикла без ConcurrentModificationException.

Практика

Практика
Какой метод позволяет получить доступ и к ключу, и к значению за одну итерацию без дополнительного поиска?
Какой метод позволяет получить доступ и к ключу, и к значению за одну итерацию без дополнительного поиска?
Was this page helpful?