From 64e39c118eea4fe06bf4138135c50e066f0ca308 Mon Sep 17 00:00:00 2001 From: egorzhurov Date: Sat, 11 May 2024 22:41:40 +0300 Subject: [PATCH] Move 40 article to markdown format --- lessons/java-core/040/Queue.md | 162 +++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 lessons/java-core/040/Queue.md diff --git a/lessons/java-core/040/Queue.md b/lessons/java-core/040/Queue.md new file mode 100644 index 0000000..d106f62 --- /dev/null +++ b/lessons/java-core/040/Queue.md @@ -0,0 +1,162 @@ +# Queue и вариации на тему. Часть II. Имплементации в Java + +При рассмотрении реализаций коллекций типа Queue предлагаю пройти от интерфейса `Queue` к реализациям, обращая внимание +на то, на каком этапе добавляются конкретные методы – они, зачастую, хорошо соотносятся с изученными в прошлом уроке +структурами данных. Также, в конце урока рассмотрим коллекцию, которая реализует и `Queue`, и `List`. + +## Интерфейс Queue + +Для знакомства с методами данного интерфейса предлагаю обратиться к [статье](https://metanit.com/java/tutorial/5.7.php) +(пока лишь по пункт _«Интерфейс Queue»_ включительно) + +Кроме того, рекомендую обратить внимание еще на 2 вещи: + +1. При многократном использовании методов `peek()` или `element()` подряд – каждый раз будет возвращаться один и тот же + элемент – последний в очереди. Разумеется, кроме ситуаций, когда вызов метода приводит к исключению; +2. Кроме метода `offer()`, описанного в статье, в `Queue` явно определен и один из методов + интерфейса-родителя – `Collection` – `add()`. Однако для добавления элемента в очередь рекомендуют использовать + именно `offer()` – он, в общем случае, реже бросает исключения. + +Также считаю важным напомнить, что очередь (как структура данных) декларирует лишь допустимое поведение, не описывая +способ хранения элементов. Что и позволяет описывать ее как интерфейс, а не класс. + +## Интерфейс Deque + +Еще один интерфейс, который является наследником `Queue` и описывает двусвязную очередь – с этой структурой данных мы +тоже познакомились в рамках предыдущего урока. + +Кроме того, интерфейс закладывает основу для реализации структуры данных _«Стек»_, посредством декларации нескольких +характерных для стека методов. + +Снова обращаясь к [статье на metanit](https://metanit.com/java/tutorial/5.7.php) (теперь уже к пункту _«Интерфейс +Deque»_), разберем методы, объявленные в `Deque` + +Обратите внимание, что использование методов `Queue` у наследника `Deque` также допустимо – поведение будет +реализовываться с учетом принципа FIFO: `poll()`, `element()` и `peek()` будут работать с элементом, стоящим первым в +деке, `add()` и `offer()` – добавлять элементы после последнего. + +Кроме того, метод `Collection#addAll()` в рамках наследников `Deque` будет работать как множественное добавление в хвост +очереди. Т.е., по сути, закрепляя поведение, равноценное работе этого метода в `Queue`. + +## Класс PriorityQueue + +Особенностью коллекций типа `Queue` является то, что прямой (по крайней мере, с точки зрения иерархии интерфейсов) +публичный наследник в `java.util` только один – `PriorityQueue`. Насколько односвязные очереди популярны в разработке +многопоточных решений, настолько же они непопулярны за их пределами. Если откроете иерархию наследников `Queue`, то +увидите, что большинство из них находится в пакете `java.util.concurrent` – пакете, содержащем классы для работы с +многопоточностью. Связано это, во многом, с неудобством работы с очередью, за исключением ряда узких задач. При равном, +в плане затрат на реализацию структуры, очереди дэке, последний является куда более дружелюбным в использовании. + +Вторым ироничным моментом является то, что `PriorityQueue` – не совсем каноничная очередь. Она хранит элементы +отсортированном виде: либо в соответствии с реализацией `compareTo()`, если коллекция параметризована типом, +реализующим `Comparable`, либо на основании переданного в параметры конструктора `Comparator`. Таким образом, принцип +FIFO не соблюдается в этой реализации. + +Таким образом, первым элементом в `PriorityQueue` всегда будет наименьший из всех (в соответствии с определенным +правилом сравнения), а последним – наибольший. + +Из других нюансов реализации могу отметить лишь то, что хранение элементов `PriorityQueue` реализовано через массив. + +Не могу сказать, что данная коллекция имеет большую популярность, но именно она, на мой взгляд, является единственно +допустимым для новичков примером реализации `Queue` (именно _Queue_, а не ее потомков, наследующих _Deque_). + +## Класс ArrayDeque + +В свою очередь, классической реализацией `Deque` является класс `ArrayDeque`. С точки зрения реализации (в том, что +связано с хранением элементов), он напоминает `ArrayList` – тот же массив для хранения элементов, тот же метод `grow()` +для увеличения размеров массива. Даже классические для дека (и двусвязного списка вообще) точки входа в структуру – +первый и последний узлы – на уровне полей класса представлены не узлами, а индексами массива. Это не влияет на работу с +классом, но позволяет немного глубже понять нюансы его реализации. + +Более подробно предлагаю познакомиться с `ArrayDeque` на базе все той же +[статьи на metanit](https://metanit.com/java/tutorial/5.7.php) (пункт _«Класс ArrayDeque»_) + +Обратите внимание, что доступные конструкторы, по сути, аналогичны конструкторам в `ArrayList`. + +## Ласковое телятко двух маток сосёт. LinkedList + +В одном из прошлых уроков уроков я обещал уделить внимание данному классу. Его достаточно часто любят вспоминать на +собеседованиях и просят сравнить его с `ArrayList` + +Связано это с тем, что `LinkedList` – вторая по известности реализация интерфейса `List`. Но, по иронии судьбы, этот +класс реализует и интерфейс `Deque`. В конечном итоге, `LinkedList` является классической реализацией двусвязного +списка, знакомого вам из практики предыдущего урока. + +В целом, данный класс является именно тем, чем кажется – структурой, предоставляющей методы для работы с собой как +с `List`, так и с `Deque`. + +Плохая новость состоит в том, что эти два подхода не стоит смешивать – есть шанс запутаться и написать код, в котором +каждое второе обращение к этому классу будет приводить к исключению или неожиданному результату. + +Хорошая же заключается в том, что даже если вам и придется работать с `LinkedList`, скорее всего его объект будет +присвоен переменной типа `List` или (менее вероятно) типа `Deque`. И шанса использовать методы коллекций разных типов у +вас не будет. + +Данному классу посвящена отдельная статья на metanit, но мне она кажется крайне неудачной (пометка для тех, кто +обращается к этому сайту не только в рамках моих уроков). + +В целом, я рекомендую большее внимание уделить не самому `LinkedList`, а его сравнению `ArrayList`. Эта история глубже, +чем может показаться на первый взгляд (вплоть до того, что на одни и те же вопросы относительно сравнения этих классов, +от джуниора и от сеньора будут ожидать разных по смыслу ответов). На данном этапе не стоит переживать, просто будьте +готовы, что тема может быть поводом для холивара. + +В качестве лаконичных заметок, освещающих разницу, преимущества и недостатки `ArrayList` и `LinkedList` рекомендую +первые два ответа в этом +[вопросе](https://ru.stackoverflow.com/questions/568119/%D0%9E%D1%82%D0%BB%D0%B8%D1%87%D0%B8%D0%B5-arraylist-%D0%BE%D1%82-linkedlist) + +Примерно тоже самое, но в более свободной форме (будет полезно, если вы даже минимально не знакомы с **О-нотацией**) +можно почитать [здесь](https://javarush.com/quests/lectures/questsyntax.level08.lecture05) + +## Другие реализации Queue и Deque + +В рамках предыдущий уроков я предлагал классификацию коллекций по потокобезопасности. Предлагаю не изменять традиции и +привести примеры коллекций и для свежеизученных типов. + +`Queue`: + +- Непотокобезопасная коллекция: `PriorityQueue`; +- Потокобезопасная (_Legacy_): отсутствует; +- Потокобезопасная (_java.util.concurrent_): `ConcurrentLinkedQueue`; + +`Deque`: + +- Непотокобезопасная коллекция: `ArrayDeque`; +- Потокобезопасная (_Legacy_): иерархически отсутствует. Если рассматривать _Deque_ как идеологического предка стека – + `Stack` (наследник `Vector`); +- Потокобезопасная (_java.util.concurrent_): `LinkedBlockingDeque`, `ConcurrentLinkedDeque` + +## В качестве итога + +`Queue` – самый непонятный на ранних этапах тип коллекций. В первую очередь, потому что непонятна его сфера применения. +По сути, в этом нет ничего страшного, очереди нужны в достаточно узких случаях. И большинство этих случаев очень тяжело +представить до знакомства с многопоточностью. Поэтому с точки зрения пользы данной статьи при прохождении собеседований +советую обратить внимание на иерархию и минимальное внимание уделить основным методы (характерным только +для `Queue`/`Deque`). Но обязательно разобрать сравнительный анализ `ArrayList` и `LinkedList`. + +Этого будет достаточно, при условии, что вы понимаете устройство структур данных вне контекста _Collections Framework_ +(по сути, содержание [предыдущего урока](https://telegra.ph/Queue-i-variacii-na-temu-CHast-I-Struktury-dannyh-01-17)). +По крайней мере, за пределами многопоточной разработки. + +#### С теорией на сегодня все! + +![img.png](../../../commonmedia/defaultFooter.jpg) + +Переходим к практике: + +## Задача: + +Реализуйте класс _Задание_. Он должен содержать поле _Название_, состав остальных полей - на ваше усмотрение. + +Реализуйте сервис, хранящий задания. Он должен иметь методы для добавления заданий и их "выполнения". Задания должны +обрабатываться в порядке очереди. Критерием выполнения задания будем считать выведение в консоль фразы _"Задание +%название задания% выполнено!"_. + +Также добавьте вывод сообщения в консоль о том, что задание (с указанием названия) добавлено. + +> Если что-то непонятно или не получается – welcome в комменты к посту или в лс:) +> +> Канал: https://t.me/ViamSupervadetVadens +> +> Мой тг: https://t.me/ironicMotherfucker +> +> **Дорогу осилит идущий!** \ No newline at end of file