JavaRush /Курсы /Java Collections /Большая задача: Создаем сокращатель ссылок

Большая задача: Создаем сокращатель ссылок

Java Collections
6 уровень , 15 лекция
Открыта

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

— Здравия желаю, Капитан Бобров!

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

— Круто. То есть я хотел сказать, я готов, сэр. Но зачем нам это нужно?

— Как это зачем? Боец, кругом враги, нам нужно защитить канал связи. Приступай к задаче.

— Есть, реализовать сервис коротких ссылок.

— Отправляйся к нашему секретному агенту Intellij IDEA, там получишь все инструкции.

— Будет сделано, товарищ капитан!

— Приступайте.

Большая задача: Создаем сокращатель ссылок - 1
16
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (1)
Давай напишем укорачиватель Shortener. Это будет некий аналог укорачивателя ссылок Google URL Shortener (https://goo.gl), но мы расширим его функциональность и сделаем консольным. Он будет сокращать не только ссылки, но и любые строки. Наш Shortener – это класс, который может для любой строки вернут
9
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (2)
Укорачиватель Shortener будет поддерживать разные стратегии хранения данных (строк и их идентификаторов). Все эти стратегии будут наследоваться от интерфейса StorageStrategy. Почитай подробнее про паттерн Стратегия на Вики. Наше хранилище будет оперировать двумя понятиями: ключ и значение. Ключом бу
16
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (3)
Вернемся к классу Shortener: 3.1. Добавь в него поле Long lastId. Проинициализируй его нулем. Это поле будет отвечать за последнее значение идентификатора, которое было использовано при добавлении новой строки в хранилище. 3.2. Добавь поле StorageStrategy storageStrategy в котором будет храниться ст
16
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (4)
Нам потребуется несколько вспомогательных классов: 4.1. Создай класс Helper. 4.1.1. Добавь в него статический публичный метод String generateRandomString(), который будет генерировать случайную строку. Воспользуйся для этого классами SecureRandom и BigInteger. Подсказка: гугли запрос "random string java".
16
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (5)
Давай напишем наше первое хранилище (стратегию хранилища). Внутри оно будет содержать обычный HashMap. Все стратегии будем хранить в пакете strategy. 5.1. Создай класс HashMapStorageStrategy, реализующий интерфейс StorageStrategy. 5.2. Добавь в класс поле HashMap<Long, String> data. В нем будут хран
32
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (6)
Первая стратегия готова, пришло время ее протестить. Для этого: 6.1. Создай класс Solution, если ты не сделал это раньше. 6.2. Добавь в класс Solution реализации вспомогательных статических методов: 6.2.1. Set<Long> getIds(Shortener shortener, Set<String> strings). Этот метод должен для переданного
32
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (7)
Приступим к реализации второй стратегии OurHashMapStorageStrategy. Она не будет использовать готовый HashMap из стандартной библиотеки, а будет сама являться коллекцией. 7.1. Разберись как работает стандартный HashMap, посмотри его исходники или погугли статьи на эту тему. 7.2. Если ты честно выпол
32
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (8)
Добавь и реализуй класс OurHashMapStorageStrategy, используя класс Entry из предыдущей подзадачи. Класс OurHashMapStorageStrategy должен реализовывать интерфейс StorageStrategy. 8.1. Добавь в класс следующие поля: 8.1.1. static final int DEFAULT_INITIAL_CAPACITY = 16; 8.1.2. static final float DEFAU
32
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (9)
Напишем еще одну стратегию, назовем ее FileStorageStrategy. Она будет очень похожа на стратегию OurHashMapStorageStrategy, но в качестве ведер (англ. buckets) будут файлы. Я знаю, ты знаешь о каких buckets идет речь, если нет – повтори внутреннее устройство HashMap. 9.1. Создай класс FileBucket в па
32
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (10)
Создай и реализуй класс FileStorageStrategy. Он должен: 10.1. Реализовывать интерфейс StorageStrategy. 10.2. Использовать FileBucket в качестве ведер (англ. bucket). Подсказка: класс должен содержать поле FileBucket[] table. 10.3. Работать аналогично тому, как это делает OurHashMapStorageStrategy, н
16
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (11)
Как ты заметил, получение идентификатора для строки требует намного больше времени, чем получение строки по идентификатору. Это ожидаемо и следует из реализации HashMap. Давай напишем четвертую стратегию OurHashBiMapStorageStrategy, которая будет лишена этого недостатка. 11.1. Создай класс OurHashBi
16
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (12)
Задача, когда требуется создать Map, работающий в две стороны (по ключу получать значение, а по значению ключ) не такая уж и редкая. Такие коллекции уже реализованы в различных сторонних библиотеках коллекций. Одна из таких Guava от Google. 12.1. Скачай и подключи библиотеку guava версии 19.0. 12.2.
16
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (13)
Рассмотрим еще одну реализацию BiMap, на этот раз из Apache Commons Collections. 13.1. Скачай и подключи Apache Commons Collections 4.0. 13.2. Реализуй стратегию DualHashBidiMapStorageStrategy. Она должна: 13.2.1. Поддерживать интерфейс StorageStrategy. 13.2.2. Внутри иметь только одно поле data с т
32
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (14)
Мы много раз тестировали наши стратегии с помощью метода testStrategy() класса Solution. Пришло время написать настоящие юнит тесты с использованием junit. 14.1. Прочитай что такое юнит тесты. 14.2. Скачай и подключи библиотеку Junit 4.12. Разберись как ей пользоваться. Библиотека Junit зависит от б
16
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (15)
Напишем еще один тест, который проверит, что получить идентификатор для строки используя стратегию HashBiMapStorageStrategy можно быстрее, чем используя стратегию HashMapStorageStrategy. 15.1. Создай класс SpeedTest в пакете tests. 15.2. Добавь в класс метод long getTimeToGetIds(Shortener short
9
Задача
Java Collections, 6 уровень, 15 лекция
Недоступна
Shortener (16)
Что можешь сделать самостоятельно (тестов на этот пункт нет): - Добавить стратегию, основанную на работе с базой данных. Гугли JDBC. - Сделать веб сервис, который будет для любого url или строки возвращать идентификатор, а для идентификатора строку. - Написать вариант HashMap с использованием двух п
Комментарии (107)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
LiudmilaM Уровень 43 Expert
1 сентября 2025
Фуф, это было тяжко. С тестами полезный опыт Ну, и пока исходники хэшмэпа читала, чуть не повесилась
SomeBody098 Уровень 51
8 ноября 2024
Годная задача, очень всего многого узнал
Long_byte Уровень 43
2 октября 2024
я понял как устроен изнутри hashmap и примерно как работает методы но что мы пишем и что получится и где будет использоваться то что мы написали какую функцию будет исполнять данное приложение не совсем понятно
overbf bf Уровень 42
19 сентября 2023
в shortener(7) от вот этого куска в правильном решении знатно подгорело: if (key != null ? !key.equals(entry.key) : entry.key != null) return false; return value != null ? value.equals(entry.value) : entry.value == null; а вроде можно же вот так?: if (!Objects.equals(key, entry.key) || !Objects.equals(value, entry.value)) { return false; } return true; а вот так нельзя, потому что нет такого объекта null у которого мы пытаемся вызвать equals if (!key.equals(entry.key) || !value.equals(entry.value)){ return false; } return true; хммм....
И. Ж. Уровень 41
27 декабря 2023
Вот так приняло: public boolean equals(Object o) { Entry entry = (Entry) o; if (!Objects.equals(key, entry.key)) return false; return Objects.equals(value, entry.value); }
wokku Уровень 51
3 сентября 2023
Отличная задача👍 Реально много нового узнаешь, хоть и местами нереально самому допереть.
Евгений Уровень 38
7 июня 2023
Годная задача
8 апреля 2024
ЧЕм?
StrangeAngel Уровень 46
2 мая 2023
Интересная задача. Мне понравилась. Ещё бы побольше опыта работы с библиотеками и вообще бы шикарно.
Василий Чи Уровень 51
27 января 2023
Как добавить библиотеку в IntelliJ IDEA Версия Guava 19 — 2015 года, поэтому я подключил версию 31.1, с ней всё тоже прекрасно работает. Вот ссылка: Guava 31.1 Ссылка на Apache Commons Collections: Apache Commons Collections
Antonina Pitertseva Уровень 2
12 декабря 2022
Полезные ссылочки для подключения библиотеки Guava от Google: https://github.com/google/guava/wiki/Release19 https://mvnrepository.com/artifact/com.google.guava/guava/19.0 https://javadevblog.com/kak-dobavit-biblioteku-jar-fajl-v-proekt-intellij-idea.html
Олег Шукюров Уровень 41
5 декабря 2022
Ребята, т.к данная задача все таки о том как разобрать HashMap «по косточкам» авторы с ней справились не до конца, настоятельно рекомендую дочитать до конца что я напишу, не пожалеете, от этих знаний на одном из собесов ошалел человек который его проводил. Все мы знаем что позиция элемента определяется по хэшкоду объекта, да, дальше по пальцам как это? Допустим у нас есть Map <Integer, String> мы пытаемся добавить один элемент 20, “str”. При создание Map под капотом создается массив Entry[] на 16 элементов(это дефолтное значение, initial capacity можно указать свое значение в скобках при создании Map) После нашей попытки добавить элемент выше происходит вот что. Высчитывается хешкод обьекта в нашем случае 20 (Хэшкод объектов типа Integer равен их значению), сейчас самая магия - данный результат делится с остатком на длину массива 20 % 16 = 4, это и есть будущая позиция данного элемента в Map. Еще одна важная вещь, как увеличивается размер хэшмап? Все очень просто, в Map имеется такая константа LOAD_FACTORY которая равняется 0.75, после каждого добавления элемента сначала проверяется длина внутреннего массива Entry[] если количество элементов в Map в нашем случае 16 * LOAD_FACTORY = 12, больше или равно 12, размер внутреннего массива удваивается и ВСЕ ПОЗИЦИИ наших элементов ПЕРЕСЧИТЫВАЮТСЯ по описанному мною выше сценарию. Т.е теперь если добавлять наш Integer (20) позиция в таблице будет равна 20 % 32 = 20; HashSet действует по такому же принципу.
Евгений Уровень 38
24 мая 2023
Чувак, спасибо, господи, где ты раньше был?
Нейросеть Уровень 41
21 августа 2023
Это ещё не магия. Заглянул я в HashMap в идее и увидел такую строчку

 if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = new Node(hash, key, value, null);
Во первых эта череда присваиваний внутри всего что только вздумается уже удивила. Я даже не знал что так можно. Во вторых индекс считается по следующей формуле

(n - 1) & hash
Оказывается длинна массива в HashMap может быть только степенью двойки. Соответсвенно если к примеру по дефолту у нас 16, то

16 = 10000
Соответственно 16 - 1

15 = 011111
Если взять пример выше и предположить, что метод hash() вернул 20(хотя в хэше мапы прячется магия не менее бодрая), то получаем

20 == 10100
(16 - 1) & 20 == 011111 & 10100 == 00100 == 4 
wokku Уровень 51
2 сентября 2023
Всё так, но начиная с java 8 создается массив Node, а не Entry.