Вы достигли нового уровня

Уровень 36

1. MVC

- Привет, Амиго!

- Привет, Билаабо!

- Ты уже серьезный программист. Поэтому сегодня у нас будет лекция про MVC.

MVC расшифровывается как Model-View-Controller. Это принцип построения архитектуры большого приложения, при котором оно разбивается на три части.

Первая часть содержит всю бизнес-логику приложения. Такая часть называется Модель (Model). В ней содержится код, который делает все то, для чего приложение создавалось. Эта часть наиболее независимая от остальных.

Вторая часть содержит все, что касается отображения данных пользователю. Такая часть называется Вид (View). Именно в ней содержится код, который управляет показом окон, страниц, сообщений и т.д.

Третья часть содержит код, который занимается обработкой действий пользователя. Любые действия пользователя, направленные на изменения модели должны обрабатываться тут. Такая часть называется Controller.

Такой подход позволяет независимо делать три вещи: логику программы (Model), механизм показа всех данных программы пользователю (View), обрабатывать ввод/действия пользователя (Controller).

Очень часто у приложения бывает несколько View. Это нормально. Вы можете видеть в Excel одни и те же данные в виде чисел и диаграмм. В играх вы можете смотреть на события от лица героя, на карте, со стороны и т.д. Все это – это разные View для одной Model.

В Controller'е сосредоточен весь код, который принимает решение – что менять в модели в ответ на действия пользователя. Например, пользователь принял решение закрыть программу, тогда надо сохранить данные модели в файл на диск. Или пользователь ввел новые данные, тогда надо добавить их в модель, а модель потом уведомит все View, об изменении данных, чтобы они отображали только актуальное их состояние.

- Еще раз.

- С точки зрения Java-программиста, можно сказать, что Model, View, Controller – это три группы классов, у которых:

а) у каждой части есть свое назначение;

б) связи между классами одной группы довольно сильные;

в) связи между группами довольно слабые;

г) способы взаимодействия частей довольно сильно регламентированы.

И вот тебе еще такая картинка:

Модель – это самая независимая часть системы. Она не зависит от View & Controller. Модель не может использовать классы из разделов View & Controller(!).

Основное ограничение вида (View) – вид не может менять модель. Классы вида могут обращаться к модели за данными или подписываться на события, но менять модель классы View не могут.

Основное ограничение контроллера – он не занимается отображением данных. Контроллер обрабатывает действия пользователя и меняет в соответствии с ними модель.

- А зачем это мне?

- То, что ты это не применяешь сейчас, еще не значит, что не будешь применять в ближайшем будущем. Ты же тут учишься, для того, чтобы работать пойти. И даже если эти знания не пригодятся тебе во время обучения, они точно пригодятся тебе во время работы.

А ведь тебя ждут еще реальные проекты и собеседования…

Мы тут сейчас с тобой разговариваем, а может через месяц ты уже работать будешь.

- Ты абсолютно прав, Билаабо. Я тебя внимательно слушаю.

- Подход MVC в архитектуре приложений встречается довольно часто. Ты должен это знать, чтобы вдруг не начать добавлять классы для View в Model, т.к. так удобнее.

Самое главное в любом проекте – это его архитектура. Твоя задача на нынешнем этапе не столько суметь сделать хорошую архитектуру, как научиться разбираться в чужой. До своей ты еще потом пару лет расти будешь. А чужую понимать надо. Сразу.

Когда в приложении используется стандартная архитектура, все становится гораздо понятнее. Зная архитектуру, ты знаешь, что где лежит, как все взаимодействует, как примерно программа работает, где добавить нужный тебе класс и где искать причину ошибки.

Но, вот если ты не знаком со стандартными подходами к архитектуре, то тебе даже самая лучшая архитектура ни о чем не скажет. Будешь смотреть как крестьянин 15 века на новый автомобиль. Стандартный автомобиль.

- Ясно. Спасибо, за интересную лекцию, Билаабо.

Вот тебе напоследок пара хороших ссылок, обязательно посмотри:

Ссылка на дополнительный материал

2. Java Beans

- Вот ты где.

Я тут подумал и решил устроить тебе еще одну маленькую лекцию, которая будет тебе очень полезна. Пока ты не работал программистом, ты, скорее всего, не сталкивался со специальной терминологией, и я хочу тебя сейчас познакомить с несколькими распространёнными понятиями.

Лет 10 назад массовое распространение получила концепция EJBEnterprise Java Beans.

- А что значит Java Beans?

- Bean по-английски боб. А Java Beans – это, стало быть, кофейные бобы (Java – сорт кофе). Такой айтишный юмор.

Бизнес логику программы представляли в виде набора высокоуровневых объектов – бинов, которые умели обмениваться сообщениями, сохранять себя, находить друг друга по имени, и еще кучу всего. Обычно это достигалось за счет специального супер-навороченного родительского класса, хотя были и другие подходы. Поведение таких объектов очень регламентировалось.

Три самых известных вида EJB-бинов:

Entity Bean – бин, цель которого хранить некоторые данные. В логику такого бина встроен механизм сохранения себя и своих полей в базу данных. Такой объект может быть уничтожен, а потом воссоздан из базы заново. Но кроме хранения данных у него нет никакой логики.

Session Bean – это функциональный бин. У каждого Session Bean есть своя функция. Один делает одно, другой другое. Такие бины работают с другими объектам и бинами, а не со своими данными.

Session Beans делятся на две категории.

Stateless Session Bean – это бин, который не хранит во внутренних переменных важных данных, нужных для его работы. Такой бин можно уничтожить, а затем заново создать, и он будет выполнять свою функцию, как и раньше.

Statefull Session Bean – это бин, который хранит у себя внутри данные, которые использует при работе. Если мы вызываем методы этого бина, то в каждом следующем вызове он может использовать часть данных, переданных ему в предыдущих. И все равно этот бин – это не то же самое, что обычный объект.

Но в использовании бинов тоже было не все так радужно, поэтому скоро маятник качнулся в обратную сторону. И разработчики стали все чаще использовать обычные объекты. Им даже придумали специальное название.

POJO (Plain Old Java Object) – старый обычный Java-объект. Такие объекты не обладали какими-то суперфункциями и не наследовались от суперобъектов. Самые обычные Java-объекты.

Когда ты познакомишься с EJB на практике, ты поймешь, в чем разница. Грубо говоря, POJO – это нож, а EJB – это швейцарский нож, по которому можно еще и звонить.

- Интересное сравнение.

- Да, вот еще что.

Со временем в назначении объектов/классов возникла специализация. Как результат – выделились некоторые роли, объекты которых получили новые названия.

DTO - Data Transfer Object – объект, который создается с целью быть использованным при транспортировке данных. Обычно к таким объектам два требования: а) уметь хранить данные, б) уметь сериализоваться. Т.е. их используют только для пересылки данных.

Создал объект, записал в него нужные данные из бизнес-логики, сериализовал в JSON/XML и отправил куда-надо. Или наоборот – пришло сообщение – десериализовал его в DTO-объект и вытягивай из него данные.

Entity – это объект, который хранится в базе данных. Но они не содержат никакой бизнес-логики. Можно сказать, что это – данные бизнес-модели.

Есть еще DAO – Data Access Object. Задача DAO - сохранять объекты в базу и доставать их из нее. Entity сам такой работой не занимается – он не содержит никакой логики и, следовательно, не может ничего никуда сохранять.

Пример:

Взаимодействие DAO и Entity
UserEntity user = UserDAO.getUserById("1535");
if (user.getAge()>18)
{
 user.setMobilization(true);
 UserDAO.save(user);
}
Комментарии
UserEntity – это класс, который хранит данные о пользователе (User-Entity)
UserDAO – это класс, который достает данные (объекты UserEntity) из базы и сохраняет их туда, после изменений.

На этом все.

Пусть это и небольшая ознакомительная лекция, но больше ты сейчас все равно не поймешь. Каждую из этих тем можно днями рассказывать, а EJB – годами.

Но я хочу, чтобы ты хотя бы представлял, о чем речь, если столкнёшься с такими вещами в разговоре, переписке, на форуме или на собеседовании.

- Гм. Спасибо, Билаабо. Да, думаю, технических терминов мне не хватает. Спасибо большое тебе еще раз.

3. Интерфейсы коллекций

- Привет, Амиго!

- Привет, Элли!

- Сегодня мы разберемся с устройством коллекций раз и навсегда.

- Давно этого ждал.

- Ты уже знаешь, что такое коллекции, и знаешь, как с ними работать. Пришло время выстроить твои знания в строгую систему. Тогда отпадут многие вопросы, почему, как, зачем и большинство вещей станет очевидно.

Интерфейсы коллекций. Структура наследования интерфейсов коллекций выглядит примерно так:

Обрати внимание, на две вещи.

Во-первых, все, что ты тут видишь – это интерфейсы.

Во-вторых, красные стрелочки обозначают «наследуется от».

- Т.е. List, Set, Queue наследуются от Collection, а Map – нет?

- Ага. Потом от этих интерфейсов наследуются абстрактные классы, а от них в свою очередь – известные тебе реализации: ArrayList, Hashtable, TreeSet,…

- Есть такое дело.

Теперь давай посмотрим, что за методы есть у этих интерфейсов:

Методы интерфейса Iterable<E>:

МетодыОписание
Iterator<T> iterator(); Возвращает объект-итератор.

- Маловато как-то.

- Ну, сколько есть. Все ради итераторов, прямо сейчас мы их рассматривать не будем, но скоро уже рассмотрим во всех подробностях.

Методы интерфейса Collection<E>:

МетодыОписание
boolean add(E e); Добавляет элемент в коллекцию
boolean addAll(Collection<? extends E> c); Добавляет элементы в коллекцию
void clear(); Удаляет все элементы из коллекции
boolean contains(Object o); Проверяет – есть ли в коллекции элемент?
boolean containsAll(Collection<?> c); Проверяет – есть ли в коллекции элементы?
boolean equals(Object o); Сравнивает коллекции
int hashCode(); Возвращает хэш-код
boolean isEmpty(); Проверяет – пуста ли коллекция?
Iterator<E> iterator(); Возвращает объект-итератор
boolean remove(Object o); Удаляет элемент из коллекции
boolean removeAll(Collection<?> c); Удаляет элементы из коллекции
boolean retainAll(Collection<?> c); Удаляет все элементы, которых нет «с»
int size(); Возвращает размер коллекции
Object[] toArray(); Преобразовывает коллекцию к массиву
<T> T[] toArray(T[] a); Преобразовывает коллекцию к массиву

- Тут уже посолиднее все. Половину из этих методов я использовал, а со второй сталкивался.

- Отлично, тогда продолжим.

Методы интерфейса List<E>:

МетодыОписание
void add(int index, E element); Добавляет элементы в середину коллекции
boolean addAll(int index, Collection<? extends E> c); Добавляет элементы в коллекцию
E get(int index); Возвращает элемент по номеру
int indexOf(Object o); Возвращает индекс(номер) элемента
int lastIndexOf(Object o); Возвращает последний индекс элемента.
ListIterator<E> listIterator(); Возвращает итератор для списка
ListIterator<E> listIterator(int index); Возвращает итератор для списка
E remove(int index); Удаляет элемент по индексу
E set(int index, E element); Устанавливает новое значение по индексу
List<E> subList(int fromIndex, int toIndex); Возвращает под-коллекцию

- Тоже ничего кардинально нового. Я уже практически все знаю по коллекциям, что не может не радовать.

- Ну, я думаю, у меня найдется, чем тебя удивить. Но давай продолжим изучат интерфейсы:

Методы интерфейса Set<E>:

МетодыОписание
нет методов

Интерфейс Set не содержит новых методов, только унаследованные.

- Да, я смотрю, интерфейс Interable был еще ничего.

С другой стороны – меньше методов – меньше запоминать!

- Меня радует твой жизнеутверждающий оптимизм.

У интерфейса Set есть два интерфейса-наследника с методами: SortedSet и NavigableSet, но я не буду их приводить, а то мы никогда не закончим.

Давай я лучше дам тебе общую картину классов и интерфейсов, описывающих коллекции в Java.

- Давай.

- Тогда держи:

- Ничего себе, да она просто огромная!

- Ну, не такая уж она и большая. Кроме того, запоминать абстрактные классы совсем не обязательно. А вот какой класс, какие интерфейсы реализует лучше запомнить – такое может пригодиться довольно часто.

- Ну, еще хотелось бы отметить, что некоторые коллекции были признаны устаревшими.

- Это какие же?

- Это я про классы Vector, Stack, Dictionary, Hashtable – они являются синхронизированными (потокобезопасными) версиями обычных коллекций.

Но в Java появилась специальная библиотека - concurrency, где содержится очень много коллекций, к которым не только можно обращаться из других потоков/нитей, но и которые написаны гораздо эффективнее. ConcurrentHashMap гораздо эффективнее Hashtable.

Использовать коллекции Vector, Stack, Dictionary, Hashtable можно, но не рекомендуется.

- Ясно, буду иметь в виду.

Спасибо, Элли!

4. Задачи

- Привет, Амиго.

- Привет, Диего. Что это у тебя за китайские иероглифы с кнопкой в центре?

- Это же всем известный MVC! Ты обязан его знать. Так, быстро иди разбираться в шаблоне MVC. Думаю, после решения задачи ты поймешь, что скрывается под иероглифами. Я дам тебе одну легенькую задачу и другую большую посложнее. Как решишь легкую, так и приступай к большой.

5. Реализации интерфейса List

- Если ты думаешь, что на интерфейсе List все закончилось, то ты ошибаешься, все только начинается. Давай я тебе расскажу про коллекции LinkedList и ArrayList.

Начну с коллекции ArrayList.

Вот как эта коллекция выглядит на схеме наследования:

Зеленым отмечены интерфейсы.

Фиолетовым – абстрактные классы.

Красным – обычные классы.

Сплошная линия – наследование, пунктирная – реализация интерфейса.

Это самая простая коллекция. Внутри ArrayList хранит элементы в простом массиве.

Основное преимущество такой коллекции над массивом – это расширяемость – увеличение длины при надобности.

Если в этом массиве заканчивается место, то создаётся второй массив побольше, куда копируются все элементы из первого. Затем второй массив занимает место первого, а первый – выбрасывается (будет уничтожен сборщиком мусора).

- А насколько увеличивается массив?

- Длина нового массива рассчитывается так (3*n)/2+1, где n – это длина старого массива. Т.е. если старый массив был длиной 100 элементов, то новый будет 300/2+1 = 151.

При добавлении элемента в середину ArrayList, все элементы справа от него копируются на 1 позицию вправо, а затем в пустую ячейку добавляется новый элемент.

При удалении элемента из середины, все элементы справа от него копируются на 1 позицию влево.

- Ты говоришь, что ArrayList сам удлиняется, при добавлении элементов в него. А при удалении он сам укорачивается?

Нет, сам внутренний массив в ArrayList никогда не укорачивается, но можно заставить ArrayList уменьшить свой внутренней массив до минимального уровня, если вызвать метод trimToSize().

Ну, и конечно, расскажу о LinkedList.

Вот как выглядит его схема наследования:

Зеленым отмечены интерфейсы.

Фиолетовым – абстрактные классы.

Красным – обычные классы.

Сплошная линия – наследование, пунктирная – реализация интерфейса.

Как ты уже знаешь, LinkedList хранит элементы в виде связного списка.

- Я постоянно об этом слышу, но не могла бы ты рассказать, что это такое?

- Конечно. Все просто.

Связный список состоит из одинаковых элементов, которые а) хранят данные, б) хранят ссылки на следующий и предыдущий элементы.

Вот так бы выглядел класс такого элемента для хранения строк:

ПримерОписание
class LinkedListElement
{
 String data;
 LinkedListElement next;
 LinkedListElement previous;
}
data хранится строковое значение элемента.
next хранит ссылку на следующий элемент в списке.
previous хранит ссылку на предыдущий элемент в списке.

А если использовать generics для типа данных, то будет что-то типа такого:

Класс элемента связного списка с generic'ом
class LinkedListElement<T>
{
 T data;
 LinkedListElement<T> next;
 LinkedListElement<T> previous;
}

- Ясненько.

- Давай, для лучшего понимания, напишем код, где добавим в двусвязный список 10 элементов:

Пример
public static void main(String[] args)
{
 LinkedListElement<Integer> tail; //хвост (самый последний элемент) списка

 for(int i=0;i<10;i++)
 {
  LinkedListElement<Integer> element = new LinkedListElement<Integer> ();
  element.data = i;

  if (tail == null) //если в хвосте нет элементов, сделать наш элемент последним
  {
   tail = element;
  }
  else //если хвост есть, добавить элемент
  {
   tail.next = element; //добавляем хвосту ссылку на следующий элемент
   element.previous = tail; //добавляем новому элементу ссылку на хвост
   tail = element; //объявляем новый элемент хвостом.
  }
 }
}

Допустим у нас в списке 10 элементов, вот как вставить элемент в середину:

Ярко красным выделены ссылки, которые поменялись – они теперь указывают на новый элемент.

Ярко фиолетовым выделены новые ссылки – ссылки нового элемента на его соседей.

А теперь – кодом:

Вставка элемента в середину связного списка
//тут содержится элемент – голова списка
LinkedListElement<Integer> head = …

//получаем 4-й элемент (нумерация с нуля)
LinkedListElement<Integer> element4 = head.next.next.next.next;
//получаем 5-й элемент
LinkedListElement<Integer> element5 = element4.next;

//Создаем новый элемент, который будем вставлять
LinkedListElement<Integer> newElement = new LinkedListElement<Integer>();
newElement.data = -18;

//обмениваемся ссылками с элементом слева
newElement.previous = element4;
element4.next = newElement;

//обмениваемся ссылками с элементом справа
newElement.next = element5;
element5.previous = newElement;

- Спасибо, Элли, действительно узнал о списках много нового.

6. Задачи

Задачи
1. Найти класс по описанию

1. Реализует интерфейс List
2. Является приватным статическим классом внутри популярного утилитного класса
3. Доступ по индексу запрещен - кидается исключение IndexOutOfBoundsException
4. Используйте рефлекшн, чтобы добраться до искомого класса
2. Поиск класса по описанию

Замените следующие слова на нужные:
1. ClassNameToBeReplaced - имя класса, потокобезопасный аналог ArrayList, в котором все операции изменения (mutative operations) используют новую копию основного массива.
2. methodNameToBeReplaced - имя метода, который в текущий список 'list' добавляет те элементы переданной коллекции, которые не содержатся в 'list'.
Не оставляйте комментированный код.

7. Деревья, красно-черные деревья

- Привет, Амиго!

- Здорово, Риша!

- Нашел тут свои старые записи и приготовил для тебя немного интересного материала. Думаю, тебе будет интересно послушать.

- Давай. Ты всегда находишь что-то интересное, которое потом становится очень полезным.

- Ладно. Сегодня я хочу тебе рассказать про деревья, поэтому начну я с графов.

Граф – это система, состоящая из точек и линий, которые их соединяют. Точки называются вершинами графа, а линии – ребрами графа. Пример:

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

Например, вершины – это города, а ребра – это дороги. Тогда поиск самой короткой дороги между городами превращается в задачу «дан граф, найти кратчайший путь между двумя вершинами».

Но не всегда путь из А в Б, занимает столько же, как и путь из Б в А. Поэтому иногда желательно бы иметь две различные линии. Для этого линии (ребра графа) заменяют на стрелки. Т.е. граф может содержать две стрелки: одну из А в Б, а вторую из Б в А.

Если в графе используются стрелки, его называют ориентированным графом, если просто линии – неориентированным графом.

У каждой вершины может быть свое количество ребер. Так же вершина может не иметь ребер вообще. Или наоборот, быть соединена ребрами со всеми остальными вершинами. Если в графе каждая вершина соединена ребром с каждой – такой граф называют полным.

Если в графе по ребрам можно добраться до любой вершины, такой граф называют связным. Граф состоящий из трех отдельных вершин, без ребер вообще, это все равно граф, но несвязный.

Чтобы соединить в связный граф N вершин, надо минимум N-1 ребер. Такой граф называется деревом.

При этом обычно одну вершину выбирают корнем дерева, а остальные объявляют ее ветвями. Ветви дерева, которые не имеют своих ветвей, называют листьями. Пример:

Дерево называют бинарным, если у каждого элемента дерева два потомка. Т.е. их может быть 0 или 2. Выше справа как раз изображено бинарное дерево.

Дерево называют полным бинарным деревом, когда у каждой ветви 2 потомка, а все листья (без потомков) находятся в одном ряду.

Пример:

- А зачем нужны такие деревья?

- О, деревья применяются много где. Бинарные деревья так вообще являются отсортированной структурой данных.

- Это как?

- Да очень просто. В каждой вершине мы храним некоторое значение. А для каждого элемента вводится правило – значение, которое хранится в потомке справа, больше, чем значение в вершине, а значение, которое хранится в потомке слева – меньше чем значение в вершине. Такое упорядочивание позволяет очень быстро находить нужные элементы в дереве.

- А можно поподробнее.

- Сортировка элементов дерева обычно выполняется добавлением. Вот, допустим, у нас есть 7 элементов: 13, 5, 4, 16, 8, 11, 10

Вот как добавляются элементы в такое дерево.

Шаг 1 – добавляем 13

Шаг 2 – добавляем 5

Шаг 3 – добавляем 4

Шаг 4 – добавляем 16

Шаг 5 – добавляем 8

Шаг 6 – добавляем 11

Шаг 7 – добавляем 10

Если мы ищем, например число 7 в таком дереве, то поиск будет проходить так:

0) Начинаем с корня.

1а) Число 7 равно 13? Нет

1б) Число 7 больше 13? Нет, тогда идем в левое поддерево.

2а) Чисто 7 равно 5? Нет.

2б) Число 7 больше 5? Да, тогда идем в правое поддерево.

3а) Число 7 равно 8? Нет

3б) Число 7 больше 8? Нет, тогда идем в левое поддерево.

4а) Левого поддерева нет, значит, числа 7 в дереве нет.

- Ага. Т.е. нам надо проверять только вершины на пути от корня до предполагаемого места нужного числа. Да, это действительно быстро.

- Еще бы, если дерево сбалансировано, то для миллиона элементов понадобится обход всего около 20 вершин.

- Да, согласен, что это не много.

А что значит – сбалансированное дерево?

- Дерево без «перекосов» - без длинных ветвей. Ведь если бы мы подавали элементы при строительстве дерева в уже отсортированном порядке, у нас бы получилось длинное-предлинное дерево, состоящее из одной ветви.

- Гм. Действительно. И как тогда быть?

- Как ты уже, наверное, догадался, самым эффективным будет дерево, которое имеет ветви примерно равной длины. Тогда при каждом сравнении отбрасывается наибольшая часть поддерева из оставшегося.

- Т.е. нужно переделать дерево?

- Ага. Его нужно «сбалансировать» - сделать максимально похожим на полное бинарное дерево.

Для решения этой проблемы были придуманы самобалансирующиеся деревья. Когда, после добавления элемента, в дереве возникает перекос, оно немного меняет порядок элементов и все становится ок. Пример балансировки:

Одними из таких деревьев есть так называемые «красно-черные деревья».

- А почему их называют красно-черные?

- Их создатель придумал красить все вершины в два цвета. Один цвет – красный, второй – черный. И разные вершины подчиняются разным правилам. На этом и стоится вся балансировка.

Пример:

- А что это за принципы?

- 1) Красная вершина не может быть сыном красной вершины.

2) Черная глубина любого листа одинакова (черной глубиной называют количество черных вершин на пути из корня).

3) Корень дерева черный.

Я не буду рассказывать тебе, как это работает, у тебя уже небось голова кипит.

- Ага. Процессор греется и не слабо так.

Вот тебе ссылка, если захочешь – почитаешь тут подробнее.

Ссылка на дополнительный материал

А теперь – иди отдыхай.

8. Задачи

Задачи
2. Использование TreeSet

Первым параметром приходит имя файла: файл1. файл1 содержит только буквы латинского алфавита, пробелы, знаки препинания, тире, символы перевода каретки.
Отсортировать буквы по алфавиту и вывести на экран первые 5 различных букв в одну строку без разделителей.
Если файл1 содержит менее 5 различных букв, то вывести их все. Буквы различного регистра считаются одинаковыми.
Регистр выводимых букв не влияет на результат. Закрыть потоки.

Пример 1 данных входного файла:
zBk yaz b-kN

Пример 1 вывода:
abkny

Пример 2 данных входного файла:
caAC A, aB? bB

Пример 2 вывода:
abc

Подсказка: использовать TreeSet
1. Разбираемся в красно-черном дереве

Дана реализация красно-черного дерева. Некоторые методы сломаны. Разберитесь в коде и исправьте ошибки.
Метод main не участвует в тестировании.
Все модификатры правильные. Имена переменных и методов не изменяйте.

9. Реализации интерфейса Set, Queue

- Ну как твой процессор?

- Норм. Посидел час в жидком азоте и как новенький!

- Отлично. Тогда давай продолжим.

Коллекции Set.

Set переводится как множество. А множество, с математической точки зрения, - это набор уникальных элементов. Но т.к. не все программисты – математики, то обычно говоря, что Set – это коллекция уникальных элементов, или коллекция, которая не позволяет хранить одинаковые элементы.

Не знаю, давала Элли тебе иерархию наследования для Set, но если нет, то вот она:

HashSet – это коллекция, которая для хранения элементов внутри использует их хэш-значения, которые возвращает метод hashCode().

Для простоты внутри HashSet<E> хранится объект HashMap<E, Object>, который и хранит в качестве ключей значения HashSet.

- Ничего себе!

- Использование hash-кодов позволяет довольно быстро искать, добавлять и удалять элементы из множества (Set).

Но учти, чтобы объекты твоих классов можно было класть в Set и правильно находить их там, у твоего класса должны быть правильно реализованы методы hashCode & equals.

И тот и другой активно используются внутри HashSet/HashMap.

Если ты забудешь реализовать метод hashCode(), то рискуешь, что твой объект в коллекции Set не будет найден, даже если он там есть.

- Да, помню, я помню. Ты мне уже рассказывал раньше об этом. Все робоуши прожужжал.

- Ок. Тогда вот тебе еще полезная информация.

Допустим, ты правильно реализовал hashCode и equals в своем классе и такой весь радостный хранишь их в Set'е.

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

А это значит, что когда ты будешь его искать в Set'е, его скорее всего не найдут.

- Ничего себе! Это как же?

- Это всем известный косяк с работой хешей. Грубо говоря, поиск в HashSet (и в HashMap) гарантированно работает правильно, только если объекты – immutable.

- Ничего себе! И что, никто ничего не делает?

- Все делают вид, что проблемы не существует. Но на собеседованиях это частенько спрашивают, так что возможно, тебе стоит запомнить этот факт…

LinkedHashSet – это HashSet, в котором элементы хранятся еще и в связном списке. Обычный HashSet не поддерживает порядок элементов. Во-первых, официально его просто нет, во-вторых, даже внутренний порядок может сильно поменяться при добавлении всего одного элемента.

А у LinkedHashSet можно получить итератор и с его помощью обойти все элементы именно в том порядке, в котором они добавлялись в LinkedHashSet.. Не часто, но иногда это может очень понадобится.

- Ясно. Люблю, когда у классов есть такие «на всякий случай» разновидности. Обычно такие случаи наступают не так уж и редко.

- TreeSet – это коллекция, которая хранит элементы в виде упорядоченного по значениям дерева. Внутри TreeSet<E> содержится TreeMap<E, Object> который и хранит все эти значения. А этот TreeMap использует красно-черное сбалансированное бинарное дерев для хранения элементов. Поэтому у него очень быстрые операции add, remove, contains.

- Ага. Я помню, мы же совсем недавно это разбирали. А я еще думал – и где это применяется.

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

- Ага, кстати, на собеседованиях часто спрашивают про TreeSet. Обычно стараются подловить. Мол, если в TreeSet используется бинарное дерево, то тогда все элементы могут образовывать одну длинную ветку и при этом поиск будет очень долгим. Тут, кстати, стоит поставить такого наглеца на место, и заявить, что даже ребенку известно, что TreeSet и TreeMap используют сбалансированные красно-черные бинарные деревья, и поэтому такая ситуация невозможна в принципе.

- Ага. Хотелось бы увидеть лицо спрашивающего в этот момент. Я, пожалуй, даже заучу эту фразу. ...

А в принципе, Set оказался не таким уж и простым, как мне казалось в самом начале.

- Зато с Queue ситуация гораздо проще:

Queue – это очередь. Элементы добавляются в конец очереди, а выбираются из ее начала.

PriorityQueue – это фактически единственная классическая реализация интерфейса Queue, не считая LinkedList, который формально тоже является очередью.

Ладно, что-то я устал, на сегодня все. Давай до свидания.

10. Задачи

Задачи
2. Найти класс по описанию

1. Реализует интерфейс Queue
2. Используется при работе с трэдами
3. Из этой очереди элементы могут быть взяты только тогда, когда они заэкспарятся, их время задержки истекло
4. Головой очереди является элемент, который заэкспарился раньше всех.
1. Осваиваем ClassLoader и Reflection

Аргументом для класса Solution является абсолютный путь к пакету, например: "C:\JavaRushHomeWork\src\com\javarush\test\level36\lesson10\bonus01\data\second".
Имя пакета может содержать File.separator. В этом пакете находятся только скомпилированные классы.
Известно, что каждый класс имеет конструктор без параметров и реализует интерфейс HiddenClass.
Считайте все классы с файловой системы, создайте фабрику - реализуйте метод getHiddenClassObjectByKey.
Известно, что есть только один класс, простое имя которого начинается с String key без учета регистра.

11. Учимся гуглить

- Привет, Амиго!

Продолжаем наши уроки – учимся гуглить.

Вот тебе несколько заданий:

 Надо найти в Google
1 Что такое граф?
2 Что такое дерево из теории графов?
3 Что такое бинарное дерево?
4 Что такое красно-черное дерево?
5 Что такое MVC?
6 Что такое EJB?
7 Что такое DAO и DTO?
8 Устаревшие коллекции в java?
9 Чем отличается TreeMap и HashMap?
10 Чем отличается TreeSet и HashSet?

12. Профессор дает доп. материал

- Привет, Амиго!

Вот тебе дополнительный материал по теме.

Ссылка на дополнительный материал

13. Хулио

- Привет, Амиго!

- Привет, Хулио.

- Вот тебе интересный факт, человеческий мозг способен вместить от 3 до 1000 терабайт информации.

- Интересно, чем же занято место в твоей голове, Хулио?

- Как чем? Интересными видеороликами. Вот пришло время занять очередные 100 мегабайт. Я как раз нашел подходящее видео.

Оригинал видео на YouTube

14. Вопросы к собеседованию по этой теме

- Привет, Амиго!

 Вопросы к собеседованиям
1 Что такое MVC?
2 Что такое DAO и DTO?
3 Что такое POJO?
4 Что такое Entity?
5 Какие коллекции-списки вы знаете?
6 Какие коллекции-множества вы знаете?
7 Что такое map, чем он отличается от «словаря»?
8 Что такое Queue и Dequeue?
9 Какие классы, реализующие интерфейс Queeue вы знаете?
10 Что такое дерево?

15. Большая задача

- Привет, боец!

- Поздравляю тебя с повышением уровня квалификации. Нам нужны отчаянные парни.

- Уверен, у тебя есть еще много нерешенных задач. Самое время решить парочку из них!