Интерфейс Map

Map<K, V> — хранилище пар «ключ → значение», в котором ключи уникальны. Это отдельная ветка коллекций: Map не наследует Collection, но входит в Java Collections Framework и тесно с ним связан.

Основные реализации:

  • HashMap — хэш-таблица, O(1) в среднем для get/put. Порядок не определён.

  • LinkedHashMap — сохраняет порядок вставки (или порядок доступа в режиме access-order).

  • TreeMap — отсортированная карта (NavigableMap), операции за O(log n).

  • Hashtable — устаревшая синхронизированная версия; используйте ConcurrentHashMap.

Иерархия

Map<K,V>
    ├── HashMap<K,V>
    │       └── LinkedHashMap<K,V>
    ├── SortedMap<K,V>
    │       └── NavigableMap<K,V>
    │               └── TreeMap<K,V>
    └── Hashtable<K,V> (legacy)

Основные методы

  • V put(K key, V value) — добавить/обновить

  • V get(Object key) — получить значение

  • V remove(Object key) — удалить запись

  • boolean containsKey(Object key) / containsValue(Object value)

  • Set<K> keySet() / Collection<V> values() / Set<Map.Entry<K,V>> entrySet()

  • V getOrDefault(key, defaultValue)

  • V computeIfAbsent(key, fn)

Пример 1. HashMap — справочник пинов

import java.util.HashMap;
import java.util.Map;

public class MapHashDemo {
    public static void main(String[] args) {
        Map<String, Integer> pins = new HashMap<>();
        pins.put("MOTOR_L_ENB", 6);
        pins.put("MOTOR_R_ENA", 5);
        pins.put("TRIG", 3);
        pins.put("ECHO", 7);

        System.out.println("ECHO на пине: " + pins.get("ECHO"));
        System.out.println("Есть TRIG? " + pins.containsKey("TRIG"));
        System.out.println("Всего: " + pins.size());
    }
}

Output:

ECHO на пине: 7
Есть TRIG? true
Всего: 4

Пример 2. Обход через entrySet

import java.util.HashMap;
import java.util.Map;

public class MapIterate {
    public static void main(String[] args) {
        Map<String, Integer> pins = new HashMap<>();
        pins.put("TRIG", 3);
        pins.put("ECHO", 7);
        pins.put("SERVO", 9);

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

Output:

SERVO -> D9
ECHO -> D7
TRIG -> D3

Пример 3. TreeMap — отсортированные ключи

import java.util.TreeMap;

public class MapTreeDemo {
    public static void main(String[] args) {
        TreeMap<Integer, String> sched = new TreeMap<>();
        sched.put(2000, "stop");
        sched.put(500,  "start");
        sched.put(1500, "turn");
        sched.put(1000, "forward");

        for (var e : sched.entrySet()) {
            System.out.println(e.getKey() + " ms: " + e.getValue());
        }
        System.out.println("Первое событие: " + sched.firstKey());
        System.out.println("Перед 1200 ms: " + sched.floorEntry(1200));
    }
}

Output:

500 ms: start
1000 ms: forward
1500 ms: turn
2000 ms: stop
Первое событие: 500
Перед 1200 ms: 1000=forward

Пример 4. Подсчёт частот через getOrDefault

import java.util.HashMap;
import java.util.Map;

public class MapFrequency {
    public static void main(String[] args) {
        String[] cmds = {"F", "F", "L", "F", "R", "L", "F"};
        Map<String, Integer> freq = new HashMap<>();
        for (String c : cmds) {
            freq.put(c, freq.getOrDefault(c, 0) + 1);
        }
        System.out.println(freq);
    }
}

Output:

{R=1, F=4, L=2}

Пример 5. computeIfAbsent для группировки

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MapGroup {
    public static void main(String[] args) {
        String[] kits = {"Arduino:UNO", "ESP32:WROOM", "Arduino:Mega", "ESP32:CAM"};
        Map<String, List<String>> grouped = new HashMap<>();
        for (String s : kits) {
            String[] p = s.split(":");
            grouped.computeIfAbsent(p[0], k -> new ArrayList<>()).add(p[1]);
        }
        System.out.println(grouped);
    }
}

Output:

{ESP32=[WROOM, CAM], Arduino=[UNO, Mega]}

Пример 6. Неизменяемая Map

import java.util.Map;

public class MapImmutable {
    public static void main(String[] args) {
        Map<String, Integer> pins = Map.of(
            "TRIG", 3,
            "ECHO", 7,
            "SERVO", 9
        );
        System.out.println("TRIG: " + pins.get("TRIG"));
        try {
            pins.put("X", 1);
        } catch (UnsupportedOperationException e) {
            System.out.println("Карта неизменяемая");
        }
    }
}

Output:

TRIG: 3
Карта неизменяемая

Подводные камни

  • Ключи HashMap обязаны корректно реализовывать hashCode() и equals() — иначе get() вернёт null, даже если ключ «по смыслу» есть.

  • get(key) возвращает null, если ключа нет, а также если значение равно null. Различайте: используйте containsKey().

  • TreeMap требует Comparable ключи или Comparator. null-ключи запрещены.

  • HashMap не потокобезопасен. Для многопоточного кода — ConcurrentHashMap.

  • Map.of(...) не допускает null ни в ключах, ни в значениях.

  • Изменение ключа после вставки (мутация полей, влияющих на hashCode) ломает карту.

См. также

Примечание

Материал подготовлен на основе официального руководства Oracle The Collections Framework и распространяется на условиях Oracle Free Documentation License. Тексты переписаны своими словами, примеры кода — оригинальные.