diff --git a/lessons/java-core/043/Introduction to Map.md b/lessons/java-core/043/Introduction to Map.md new file mode 100644 index 0000000..3602ce9 --- /dev/null +++ b/lessons/java-core/043/Introduction to Map.md @@ -0,0 +1,201 @@ +# Map. Первое знакомство + +В рамках текущего урока мы познакомимся с последним из типов коллекций — **Map** (словарь, сленг. — **мапа**). Разберем +методы интерфейсов, посмотрим на реализации. Но знакомство с внутренней устройством этих реализаций будет вынесено в +отдельный урок — эта тема популярна на собеседованиях и стоит разобраться с ней подробно. + +В целом, структура урока будет симметрична таковой в +[«Урок 41. Set. Первое знакомство»](https://telegra.ph/Set-Pervoe-znakomstvo-01-25). + +## Общая информация + +Все реализации `Map` являются ни чем иным, как **ассоциативным массивом**. Соответственно, любая мапа представляет собой +набор пар вида «ключ-значение», где ключ уникален, значение — нет. + +Таким образом, если все изученные нами типы коллекций имели параметризацию вида ``, то `Map` имеет параметризацию +вида ` (key, value)`. + +Именно поэтому `Map`, в отличии от остальных типов коллекций, не является наследником `Collection` и `Iterable`. Строго +говоря, интерфейс `Map` вообще ни от кого не наследуется. Поэтому ряд методов, уже привычных по остальным коллекциям, +может отсутствовать. + +С точки зрения применения, `Map` тяжело переоценить: от обычного хранения некого множества данных с доступом к элементам +по ключу до реализации более сложных структур на основании коллекций (мапа мап, мапа сетов и пр.) и коллекции, хранящей +разного рода агрегаты над первичными данными. + +Также реализации `Map` лежат в основе уже изученных реализаций Set'а. + +## Интерфейсы Map, Map.Entry + +Основа иерархии Map — интерфейс `Map`. Его методы во многом схожи с методами `Collection` или какого-нибудь из его +наследников, некоторые — с поправкой на особенности параметризации. + +В свою очередь `Entry` — вложенный интерфейс, является основной единичного значения `Map` — пары «ключ-значение». Грубо +говоря, любая мапа — набор элементов типа `Entry`. + +Более подробно с методами этих интерфейсов предлагаю ознакомиться на основании +[статьи](https://metanit.com/java/tutorial/5.8.php) (до пункта _«Классы отображений. HashMap»_) + +Кроме методов `Map`, описанных в статье, стоит также выделить несколько методов (групп методов), которые были опущены: + +- Методы, параметры которых являются **функциональными интерфейсами**. После темы коллекций мы познакомимся с ФП в Java, + в т. ч. вернемся к этим методам. В `Collection` их было меньше, у `Map` — больше. В любом случае, работать с + коллекциями можно и без них; +- Методы `replace()`. Заменяют существующее значение для указанного ключа на новое. Существуют две перегруженные + версии — заменяющая значение, если существует ключ, а также заменяющая значение по ключу, лишь если старое значение + совпадает с указанным в соответствующем параметре. +- Статические методы создания мапы (или `Entry`). Нечто похожее мы видели в `List` и `Set`. Подробнее разберем эти + методы ниже. + +Статические методы в `Map`: + +- `of()`. В отличии от `List` и `Set`, в `Map` эти методы принимают параметрами ключи и значения — каждый нечетный + параметр будет `key`, следующий за ним четный — `value`. Существуют реализации `of()` от одной до десяти пар + «ключ-значение». Перегруженная версия этого метода для _varargs_ отсутствует в силу особенностей параметризации. Также + обратите внимание: передача в такие методы `null` приведет к исключению. +- Вместо `of()` с _varargs_ есть метод `ofEntries()`, принимающий массив `Map.Entry` (в виде _varargs_) и формирующий на + их основании мапу. Не самая удобная, но альтернатива. Также, как и в `of()`, _null_-значения недопустимы. Ни в + качестве параметров (Entry), ни в качестве ключей или значений этих `Entry`; +- `copyOf()` также существует, но, в отличии от известных нам реализаций, принимает параметром не `Collection`, а `Map`. + Логичный, но, почему-то не для всех очевидный нюанс; +- И, наконец, не имеющий аналогов метод `entry()`. Создает неизменяемый объект типа `Map.Entry` на основании переданных + параметров ключа и значения. + +Для `Entry` в статье почему-то были опущены статические методы `comparingByKey()` и `comparingByValue()`. Каждый в двух +реализациях. Возвращают компараторы для сравнения `Entry` по ключу или значению соответственно. Как в естественном +порядке (в соответсвии с имплементацией `Comparable`, если она есть), так и на основании переданного параметром +компаратора. + +Также был опущен статический метод `copyOf()`, создающий новое `Entry` на основании переданного параметром. + +Работа с `Entry` напрямую — не самая лучшая практика, особенно за пределами **Stream API** (с ним мы познакомимся уже +достаточно скоро). Но иногда она является меньшим из зол. Поэтому лучше иметь общее представление о содержимом этого +интерфейса. + +## Интерфейс SortedMap + +Полагаю, на этом этапе вы уже догадываетесь, что иерархия наследования `Map` симметрична таковой у `Set`. Что, в целом, +логично, учитывая, что набор ключей мапы — то, на чем и строится основное взаимодействие с этим типом коллекций — легко +представить именно Set'ом. + +Итак, предлагаю ознакомиться с методами `SortedMap` на основании [статьи](https://metanit.com/java/tutorial/5.9.php) +(пункт _«SortedMap»_). + +Здесь все просто и, в целом, похоже на уже изученный `SortedSet`. Даже так же, как и в случае с `SortedSet`, в статье +почему-то забыт метод `comparator()`, возвращающий компаратор, на основании которого сделана сортировка. В `SortedSet` — +значений, здесь — ключей. + +Далее, продолжая аналогию, разберем наследника `SortedMap` — `NavigableMap`. + +## Интерфейс NavigableMap + +Как вы, полагаю, догадались, ничего принципиально нового вы не увидите. `NavigableMap` по составу методов +напоминает `NavigableSet`, с поправкой на особенности параметризации, из-за чего ряд методов +(аналогичных `ceiling()`, `higher()`, `floor()` и `lower()` у `NavigableSet`) дублируются для `Entry` и для ключа. + +[Пункт _«NavigableMap»_](https://metanit.com/java/tutorial/5.9.php) + +## Класс AbstractMap.SimpleEntry и другие реализации Map.Entry + +Прежде чем перейти к реализациям `Map`, предлагаю ознакомиться с основными реализациями `Map.Entry`. Благо, это не +займет много времени. + +В случае, если вам понадобится создать изменяемую `Entry` — рекомендую воспользоваться вложенным +классом `AbstractMap` — `SimpleEntry`. В том же классе можно найти и immutable-аналог — вложенный +класс `SimpleImmutableEntry`. + +Обе реализации имеют конструкторы, создающие `Entry` на основании пары «ключ-значение», а также на основании +другого `Entry`. + +Альтернативой `AbstractMap.SimpleImmutableEntry` может выступить класс `KeyValueHolder` — именно его +использует `Map.entry()`. Из минусов — `KeyValueHolder` не имеет конструктора для создания объекта на базе +другого `Entry`, а также не является сериализуемым (что это значит — разберемся в свое время). В остальном эти +реализации идентичны. + +## Класс HashMap + +Наиболее простая и, одновременно, наиболее популярная реализация `Map`. Данные не отсортированы, порядок добавления не +сохраняется. В целом, как `HashSet`, только `HashMap`:) + +Познакомиться с конструкторами и использованием можно в рамках [статьи](https://metanit.com/java/tutorial/5.8.php) ( +пункт _«Классы отображений. HashMap»_) + +## Класс LinkedHashMap + +Реализация `Map`, сохраняющая порядок добавления элементов. Соответственно, методы `keySet()`, `values()` и `entrySet()` +будут возвращать коллекции, хранящие ключи/значения/Entry в соответствии с порядком добавления. + +По аналогии с соответствующими реализациями `Set`, `LinkedHashMap` является наследником `HashMap`. Но, в отличии от +аналогичного Set'а, `LinkedHashMap` определяет ряд вложенных классов для реализации рассмотренных выше методов, а также +переопределяет ряд методов `HashMap`, что и позволяет гарантировать нужный порядок элементов. + +К слову, порядок элементов в `LinkedHashSet` тоже гарантируется именно из-за использования `LinkedHashMap` внутри. + +## Класс TreeMap + +Единственная публичная реализация `NavigableMap` в `java.util`. Строится на основе уже знакомого нам _RB-tree_. + +Как и соответствующий сет, хранит элементы в отсортированном виде (на основании `Comparable` или `Comparator` для +ключа). + +С конструкторами и использованием предлагаю познакомиться в [статье](https://metanit.com/java/tutorial/5.9.php) +(пункт _«TreeMap»_) + +## Другие реализации + +Мы рассмотрели выше основные непотокобезопасные реализации в `java.util`. Из-за возможностей, которые дают ассоциативные +массивы, публичных реализаций, заточенных под узкую специфику использования, у `Map` больше, чем у `Set`. Но в рамках +данного урока мы их затрагивать не будем, ограничимся мапами общего назначения. Зато озвучим основные потокобезопасные +реализации: + +- _legacy_: классической legacy-реализацией `Map` является `Hashtable`; +- _java.util.concurrent_: `ConcurrentHashMap` как потокобезопасный аналог `HashMap`. Реализацией `NavigableMap` и, + соответственно, аналогом `TreeMap` является `ConcurrentSkipListMap`. + +## Итог + +На данном этапе можно утверждать, что мы познакомились со всеми типами коллекций в Java. + +Это еще далеко не конец знакомства с `Collection Framework` — впереди еще разбор устройства `HashMap`, знакомство с +коллекциями из `java.util.concurrent` в контексте изучения многопоточности, обработка коллекций в рамках ФП и разбор +методов коллекций, которые были опущены на текущем этапе. + +Тем не менее, сейчас мы достигли определенной черты — текущий знаний хватит, чтобы написать полноценное однопоточное +приложение. Да, оно будет выглядеть несовременно. Да, оно вряд ли будет реализовано качественно в контексте архитектуры. +Да, это будет консольное десктоп-приложение. И да, оно сможет сохранять результат своей работы в лучшем случае в файл, а +не в БД. Но это уже результат и серьезное достижение. + +Поэтому искренне поздравляю всех, кто дошел до этого этапа. Надеюсь, мне удалось сделать ваш путь менее тернистым, чем +он был у меня и многих других разработчиков. Дальше будет… по-разному. Где-то будет удобнее и проще, где-то будет +сложнее, где-то будет взрываться мозг. Но именно на данном этапе можно утверждать, что вы познакомились с необходимым +базисом, на котором строится дальнейшее развитие Java-разработчика. + +Так держать! + +#### С теорией на сегодня все! + +![img.png](../../../commonmedia/defaultFooter.jpg) + +Переходим к практике: + +## Задача 1: + +Реализуйте программу, выводящую в консоль количество использований каждого из уникальных слов в введенной пользователем +строке. + +За основу предлагаю взять реализацию из +[задачи 3 урока 30](https://github.com/KFalcon2022/practical-tasks/tree/master/src/com/walking/lesson30_regex/task3). + +## Задача 2: + +Реализуйте +[задачу из урока 19](https://github.com/KFalcon2022/practical-tasks/tree/master/src/com/walking/lesson19_object_methods), +используя `Map`. Реализацию выберите исходя из особенностей исходной задачи. + +> Если что-то непонятно или не получается – welcome в комменты к посту или в лс:) +> +> Канал: https://t.me/ViamSupervadetVadens +> +> Мой тг: https://t.me/ironicMotherfucker +> +> **Дорогу осилит идущий!** \ No newline at end of file