Map

Основные разделы

Что это

Map(карта, словарь) - коллекция хранит не множество элементов, а множество «пар элементов».

HashMap

Bucket(array) -> Односвязный list Node<K, V>(ключ - значение) или TreeNode

Node содержит: hashCode, key, value, next.

Ключ действует так же как и в HashSet.

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

HashMap bucket будет трансформирован из односвязного списка(Node) в дерево(TreeNode) при размере > 8. И произойдёт обратное при > 6.

default initial capacity = 16 - размер массива bucket.

load_factor = 0.75 - процент на который должен быть заполнен capacity(non-empty buckets) для умножения capacity на 2.

Если во время обхода карта меняется (кроме iterator.remove()), бросается ConcurrentModificationException. Под капотом это работает через счётчик модификаций (modCount), который проверяется в каждом next().

Сложность алгоритмов

  1. Получение - от O(1)(при отсутствии коллизий), O(n) - до 8 элементов в бакете, O(log(n)) - больше 8.
  2. Вставить - от O(1)(при отсутствии коллизий), O(n) - до 8 элементов в бакете, O(log(n)) - больше 8.
  3. Удаление - от O(1)(при отсутствии коллизий), O(n) - до 8 элементов в бакете, O(log(n)) - больше 8.

Методы

  1. V put(K key, V value)
  2. V get(Object key)
  3. V remove(Object key)
  4. Set<K> keySet()
  5. Collection<V> values()
  6. Set<Map.Entry<K, V>> entrySet()
  7. boolean containsKey(Object key) – возвращает значение true, если в карте имеется указанный ключ.
  8. boolean containsValue(Object value) – Возвращает значение true, если в карте имеется указанное значение.

WeakHashMap

Самоочищающийся кэш.

HashMap часто применяют для кэширования, но он держит сильные ссылки на ключи, мешая GC освободить память даже тогда, когда объект уже больше нигде не используется. Это может привести к утечкам памяти.

WeakHashMap хранит ключи через слабые ссылки. Если на объект-ключ больше нет сильных ссылок, GC может удалить его, и запись в мапе исчезнет автоматически.

Используется для временных кэшей, хранения слушателей, метаданных, привязанных к жизненному циклу объекта.

IdentityHashMap

Когда объекты "равны", но не одинаковы.

HashMap проверяет ключи через метод .equals(). Если у вас два объекта с одинаковыми данными, но это разные экземпляры (например, два Person с одинаковым именем, но разными записями), то HashMap перезапишет значение.

IdentityHashMap сравнивает ключи только по ссылке (==), игнорируя .equals(). Разные объекты всегда будут разными ключами, даже если у них одинаковые данные.

Используется во фреймворках, парсерах и графах зависимостей, где важна физическая идентичность объекта.

EnumMap<Status, String>

Оптимизированный вариант для enum-ключей.

Эффективная key-value структура, использующая enum в качестве ключа.

Быстрее и легче, чем HashMap (например, для меток или конфигураций по статусу).

Использовать enum как ключ в HashMap неэффективно. Нужно считать хэши, обрабатывать коллизии, хотя набор ключей фиксирован на этапе компиляции.

EnumMap специально создан для enum-ключей. Внутри он работает через массив, используя ordinal() значения enum в качестве индекса. Это даёт настоящие O(1)-операции и экономит память.

Используется всегда, если ключи берутся из одного enum.

Быстрый гайд по выбору

TreeMap

Сложность алгоритмов

  1. Получение - если перый эленент - O(1), если не первый O(log(n))
  2. Вставить - O(log(n))
  3. Удаление - O(log(n))

Плюсы

  1. Высокая скорость поиска.
  2. Сортировка данных.

Что реализует

На основе бинарного(каждый узел имеет только два потомка) красно-чёрного(сбалансированного) дерева. Гарантирует сложность log(n).

Бинарное дерево состоит из

  1. Корень - верхний узел.
  2. Узлы.
  3. Листья - нижние узлы не имеющие потомков.

Красно-чёрное дерево состоит из

Красно-чёрное дерево - добавляет балансировку. Во время добавления до 2-х поворотов. При удалении до 3-х.

  1. Узел либо красный, либо черный.
  2. Корень черный.
  3. Все листья чёрные.
  4. Оба потомка красного узла - черные.
  5. Каждый простой пусть от данного узла до любого листового узла, являющегося его потомком, содержит одинаковое число черных узлов.

Как создать

Для создания объекта дерева, необходимо передать Comparator в конструктор. Или ключом должен быть класс реализующий интерфейс Comparable<T>.

Условия для key

  1. Immutable
  2. Переопределить equals и hashcode

ConcurrentHashMap

Использует сегментирование распространённые блокировки(в новых версиях — на уровне bucket), что позволяет нескольким потокам читать и писать без полной блокировки карты.

Hashtable при обращении блокировалась полностью.

HashTable и Collection.synchronized(HashMap) - Очень похожи. Но HashTable имеет функционал Enumerators(счетчики).

Пример изменения типа в Map

import java.util.*;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        Map<String, String> stringMap = new HashMap<>();
        stringMap.put("1", "100");
        stringMap.put("2", "200");

        Map<Integer, Integer> intMap = stringMap.entrySet()
            .stream()
            .collect(Collectors.toMap(
                e -> Integer.parseInt(e.getKey()),
                e -> Integer.parseInt(e.getValue())
            ));

        System.out.println(intMap); // {1=100, 2=200}
    }
}
        

ConcurrentSkipListMap

Потокобезопасная и отсортированная реализация интерфейса NavigableMap, которая работает на основе skip list(списка с пропусками). Skip list — это многослойная структура данных, состоящая из нескольких уровней связных списков: нижний уровень содержит все элементы в порядке сортировки, а верхние уровни - лишь часть элементов для ускорения поиска. При добавлении элемента случайно выбирается его "высота" (количество уровней, на которых он присутствует), что позволяет "перепрыгивать" через блоки данных на верхних уровнях и выполнять операции со сложностью O(log n). Если нужен параллельный доступ к отсортированной мапе

Skip List: Многослойный список с пропусками, где верхние уровни помогают быстро достигать нужного диапазона элементов, снижая количество переходов по связям.

Потокобезопасность обеспечивается за счёт использования CAS (Compare-And-Swap) операций и минимального блокирования. Модификации происходят безопасно без полной блокировки структуры.

Элементы всегда хранятся в отсортированном порядке по их ключам, что позволяет выполнять операции вроде поиска диапазонов или итераций по упорядоченным ключам очень эффективно.

Высокая доступность: чтение не блокируется, даже если выполняются параллельные операции вставки или удаления.

Особенности

Операции чтения (например, get или containsKey) выполняются быстро и без блокировок благодаря структуре skip list.

Операции записи (например, put и remove) синхронизированы, но производительность сохраняется за счёт оптимизации с использованием CAS.

Поддерживает натуральный порядок ключей или порядок, определённый переданным компаратором.

Эффективен для сценариев, где важны отсортированные данные и параллельный доступ, но количество обновлений не слишком велико.