В каких случаях использовать Map для данных
Что проверяют
Выбираете ли вы структуру данных по способу доступа и знаете ли, чем отличаются реализации Map. Следом почти всегда спрашивают про устройство HashMap или про то, что взять для многопоточного кода.
Как отвечать
Map нужен, когда данные ищут по ключу, а не по позиции:
- поиск по идентификатору: вместо вложенного цикла по двум спискам за O(n·m) строите
Map<Long, User>и проходите за O(n + m); - подсчёт:
counts.merge(word, 1, Integer::sum); - группировка:
computeIfAbsent(dept, k -> new ArrayList<>()).add(emp)илиCollectors.groupingBy; - кеш:
new LinkedHashMap<>(16, 0.75f, true)с переопределённымremoveEldestEntryдаёт простой LRU.
Затем реализации:
HashMap: в среднем O(1), порядок не гарантирован. С Java 8 корзина с множеством коллизий превращается в красно-чёрное дерево, худший случай O(log n). Для заранее известного размера естьHashMap.newHashMap(n);LinkedHashMap: порядок вставки или доступа;TreeMap: сортировка по ключу, O(log n),floorKey,ceilingEntry,subMapдля диапазонов;EnumMap: ключи из перечисления, внутри массив;ConcurrentHashMap: многопоточный доступ, атомарныеcomputeиmerge,nullне принимает;Map.of: неизменяемая, безnull, порядок итерации не определён.
С Java 21 LinkedHashMap и TreeMap реализуют SequencedMap с firstEntry(), lastEntry() и reversed().
Скажите и когда Map не нужен: ключи это плотные числа от нуля, тогда хватит массива; нужна только проверка наличия, тогда Set; фиксированный набор полей, тогда record, а не Map<String, Object>.
Где спотыкаются
Используют изменяемый объект как ключ и меняют его после put. hashCode меняется, запись не находится, хотя из Map она не удалена. Та же беда, если equals и hashCode переопределены несогласованно.
Берут HashMap в многопоточном коде или удаляют элементы в цикле по keySet() и получают ConcurrentModificationException. Для удаления есть removeIf или итератор.
Полагаются на порядок обхода HashMap: он меняется после рехеширования и может отличаться между версиями JDK.