Map.entrySet: примеры (JAVA)

Руководство по использованию Map.entrySet в Java
Раздел: Коллекции (Collection Framework) - Map
Map.entrySet: Set>

Описание Map.entrySet

Метод Map.entrySet() возвращает представление набором элементов пары ключ-значение в карте в виде Set<Map.Entry<K,V>>. Это не копия, а представление, связанное с исходной картой: изменения в наборе отражаются в карте и обратно при условии, что реализация карты поддерживает модификации. Набор используется для итерации по элементам, массовых операций удаления и для прямой модификации значений через Map.Entry.setValue.

Аргументы: метод без аргументов.

Возвращаемое значение: Set<Map.Entry<K,V>>. Особенности возвращаемого набора:

  • Это представление (view) данных карты, а не глубокая копия.
  • Итератор набора поддерживает remove(), который удаляет соответствующую запись из карты.
  • Записи реализованы как Map.Entry<K,V> с методами getKey(), getValue(), setValue(V). Для некоторых реализаций setValue может выбрасывать UnsupportedOperationException (например, для неизменяемых карт).
  • Семантика equals() и hashCode() для записей соответствует контракту Map.Entry, что позволяет сравнивать наборы записей между картами.
  • Поведение при параллельном доступе зависит от реализации карты (например, HashMap не потокобезопасен, ConcurrentHashMap имеет свои ограничения).

Типичные сценарии использования: перебор пар ключ-значение с доступом к ключу и значению без дополнительного поиска, удаление элементов во время итерации, массовые операции через removeIf или работа со стримами через map.entrySet().stream().

Примеры использования

Примеры показывают базовые варианты получения и использования entrySet(), с кодом и результатом.

Итерация for-each и вывод

import java.util.*;
public class Example1 {
  public static void main(String[] args) {
    Map map = new HashMap<>();
    map.put("a", 1);
    map.put("b", 2);
    map.put("c", 3);
    for (Map.Entry e : map.entrySet()) {
      System.out.println(e.getKey() + " -> " + e.getValue());
    }
  }
}
a -> 1
b -> 2
c -> 3

Изменение значения через Map.Entry.setValue

import java.util.*;
class Example2 {
  public static void main(String[] args) {
    Map map = new HashMap<>();
    map.put("x", 10);
    for (Map.Entry e : map.entrySet()) {
      e.setValue(e.getValue() + 5);
    }
    System.out.println(map);
  }
}
{x=15}

Удаление во время итерации через Iterator.remove

import java.util.*;
class Example3 {
  public static void main(String[] args) {
    Map map = new HashMap<>();
    map.put("a", 1); map.put("b", 2); map.put("c", 3);
    Iterator> it = map.entrySet().iterator();
    while (it.hasNext()) {
      Map.Entry e = it.next();
      if (e.getValue() % 2 == 0) it.remove();
    }
    System.out.println(map);
  }
}
{a=1, c=3}

Использование с потоками

import java.util.*;
import java.util.stream.*;
class Example4 {
  public static void main(String[] args) {
    Map map = Map.of("a",1,"b",2,"c",3);
    List keysWithOdd = map.entrySet().stream()
      .filter(e -> e.getValue() % 2 == 1)
      .map(Map.Entry::getKey)
      .collect(Collectors.toList());
    System.out.println(keysWithOdd);
  }
}
[a, c]

Похожие методы в Java

В Java доступны другие представления и методы, близкие по назначению к entrySet():

  • Map.keySet() - возвращает Set ключей. Предпочтителен, когда нужна только информация о ключах, без значений.
  • Map.values() - возвращает коллекцию значений. Удобен для операций только со значениями, но не сохраняет соответствие с ключами.
  • Map.forEach(BiConsumer) - выполняет действие для каждой пары, не возвращая представление. Удобен для побочных эффектов без модификации структуры коллекции.
  • Map.ofEntries(...) и Map.entry(K,V) (Java 9+) - средства для создания небольших неизменяемых карт; возвращаемые карты обычно не поддерживают setValue и бросают UnsupportedOperationException.

Выбор: если требуется итерироваться и изменять значения без дополнительных обращений по ключу, entrySet() предпочтительнее. Для простых списков ключей или значений лучше применять keySet() или values().

Аналоги в других языках

Краткие сравнения и примеры эквивалентов Map.entrySet() в популярных языках.

JavaScript

// Для объекта
const obj = {a:1, b:2};
console.log(Object.entries(obj));
// Для Map
const m = new Map([["a",1],["b",2]]);
console.log(Array.from(m.entries()));
[['a',1],['b',2]]
[['a',1],['b',2]]

Python

d = {'a':1, 'b':2}
print(list(d.items()))
# Итерация дает кортежи (key, value)
[('a', 1), ('b', 2)]

PHP

$arr = ['a'=>1, 'b'=>2];
foreach ($arr as $k => $v) { echo "$k -> $v\n"; }
a -> 1
b -> 2

C#

using System; using System.Collections.Generic;
class P { static void Main(){
  var d = new Dictionary{{"a",1},{"b",2}};
  foreach(var kv in d) Console.WriteLine(kv.Key + " -> " + kv.Value);
}}
a -> 1
b -> 2

Go

m := map[string]int{"a":1, "b":2}
for k,v := range m {
  fmt.Println(k, v)
}
a 1
b 2

Отличия от Java: в большинстве языков представление пар возвращается в виде массивов/кортежей или итераторов. В Java важна семантика view: изменения через запись в наборе отражаются в исходной карте при поддержке операции, а в многих других языках копирование является обычным поведением, либо нет аналога setValue как метода записи в паре.

Типичные ошибки и исключения

Частые проблемы при работе с entrySet() и примеры:

ConcurrentModificationException при модификации карты

import java.util.*;
class Err1 {
  public static void main(String[] args) {
    Map map = new HashMap<>();
    map.put("a",1); map.put("b",2);
    for (Map.Entry e : map.entrySet()) {
      if (e.getKey().equals("a")) map.remove("a"); // некорректно
    }
  }
}
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1493)
    ...

Правильно: использовать Iterator.remove() или collect to remove потом или removeIf.

UnsupportedOperationException для неизменяемых карт

import java.util.*;
class Err2 {
  public static void main(String[] args) {
    Map map = Map.of("a",1);
    Map.Entry e = map.entrySet().iterator().next();
    e.setValue(2); // вызовет исключение
  }
}
Exception in thread "main" java.lang.UnsupportedOperationException
    at ...

ClassCastException или Comparator mismatch для TreeMap

import java.util.*;
class Err3 {
  public static void main(String[] args) {
    Map map = new TreeMap();
    map.put(1, "one");
    map.put("two", 2); // разные типы ключей приведут к ошибке при сравнениях
  }
}
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer
    at ...

Ещё один нюанс: у ConcurrentHashMap вызов entry.setValue может быть не поддержан и бросать UnsupportedOperationException в зависимости от реализации.

Изменения в последних версиях Java

Сам метод entrySet() существует давно и существенно не менялся, но в последних релизах появились сопутствующие возможности:

  • Java 8: появление Stream API. Часто применяется map.entrySet().stream() для преобразований и фильтрации.
  • Java 9: добавлены Map.entry(K,V) и фабричные методы Map.of, Map.ofEntries. Возвращаемые карты могут быть неизменяемыми, что влияет на поведение entry.setValue и операций удаления.
  • Java 10/11: Map.copyOf для создания неизменяемых копий; у неизменяемых карт набор записей будет бросать UnsupportedOperationException при попытке модификации.

Итог: сам контракт entrySet() не изменился, но окружение и дополнительные API сделали использование более гибким и добавили факторы, которые влияют на возможность модификации возвращаемого представления.

Расширенные и редкие примеры

Несколько продвинутых сценариев с пояснениями и результатами.

Удаление по условию через removeIf

Пример java
import java.util.*;
class Adv1 {
  public static void main(String[] args) {
    Map map = new HashMap<>();
    map.put("a",1); map.put("b",2); map.put("c",3);
    map.entrySet().removeIf(e -> e.getValue() < 3);
    System.out.println(map);
  }
}
{c=3}

Атомарное обновление значений без дополнительного поиска

Пример java
import java.util.*;
class Adv2 {
  public static void main(String[] args) {
    Map map = new HashMap<>();
    map.put("k", 1);
    for (Map.Entry e : map.entrySet()) {
      e.setValue(e.getValue() * 2); // обновление in-place
    }
    System.out.println(map);
  }
}
{k=2}

Сборка новой карты из entrySet с преобразованием ключей и значений

Пример java
import java.util.*;
import java.util.stream.*;
class Adv3 {
  public static void main(String[] args) {
    Map map = Map.of("a",1,"b",2);
    Map transformed = map.entrySet().stream()
      .collect(Collectors.toMap(
        e -> e.getKey().toUpperCase(),
        e -> "val:" + e.getValue()
      ));
    System.out.println(transformed);
  }
}
{A=val:1, B=val:2}

Использование entrySet с ConcurrentHashMap для безопасных операций чтения/записи

Пример java
import java.util.concurrent.*;
class Adv4 {
  public static void main(String[] args) {
    ConcurrentHashMap chm = new ConcurrentHashMap<>();
    chm.put("a",1); chm.put("b",2);
    chm.forEach(1, (k,v) -> System.out.println(k+":"+v));
    // Прямая модификация через entry.setValue может быть неопределенной, рекомендуется compute/replace
    chm.computeIfPresent("a", (k,v) -> v + 10);
    System.out.println(chm);
  }
}
a:1
b:2
{a=11, b=2}

Использование entrySet для поиска пары с минимальным значением

Пример java
import java.util.*;
class Adv5 {
  public static void main(String[] args) {
    Map map = Map.of("x",10,"y",5,"z",7);
    Map.Entry min = map.entrySet().stream()
      .min(Map.Entry.comparingByValue())
      .orElse(null);
    System.out.println(min.getKey()+" -> "+min.getValue());
  }
}
y -> 5

Комментарий: entrySet дает доступ к ключу и значению одновременно, что позволяет избегать двух обращений к карте (get по ключу) и повышает производительность в коде с интенсивной обработкой пар.

джава Map.entrySet function comments

En
Map.entrySet Returns a Set view of the mappings contained in this map