Библиотека джависта | Java, Spring, Maven, Hibernate
23.3K subscribers
2.22K photos
47 videos
45 files
3.16K links
Все самое полезное для Java-разработчика в одном канале.

Список наших каналов: https://t.iss.one/proglibrary/9197

Для обратной связи: @proglibrary_feeedback_bot

По рекламе: @proglib_adv

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
💬 Обратная связь

Какая рубрика нравится больше? Если забыли, о чём рубрика, можно освежить в памяти тут.

🔥#CoreJava
👍🏼#Enterprise
👾#DevLife
🤔#News
❤️ → Всё нравится :))

🐸 Библиотека джависта

#DevLife
Please open Telegram to view this post
VIEW IN TELEGRAM
18👍12🔥6👾4
👀 Внутреннее устройство HashSet

HashSet — это реализация интерфейса Set на основе HashMap. Хранит уникальные элементы без дубликатов с быстрым O(1) поиском.

📦 Базовая структура

HashSet — это тонкая обёртка над HashMap:
public class HashSet<E> {
private transient HashMap<E, Object> map;
private static final Object PRESENT = new Object();

public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
}


▪️ Главные особенности

→ Элементы хранятся как ключи HashMap.
→ Значения — константа PRESENT (заглушка).
→ O(1) для add, remove, contains (как у HashMap).
→ Не гарантирует порядок элементов.
→ Не допускает дубликаты (ключи Map уникальны).

🔍 Как устроено хранение

🔹 Внутренняя структура идентична HashMap:

HashMap<E, PRESENT>:

table[]:
[0]: null
[1]: Entry(key="B", value=PRESENT) → Entry(key="M", value=PRESENT)
[2]: Entry(key="A", value=PRESENT)
[3]: Entry(key="C", value=PRESENT)
[4]: null
...

Set элементы = ключи HashMap
Значения = PRESENT (игнорируются)


🔹 Все операции делегируются HashMap

add(E element) — добавление

1. Вызывается map.put(element, PRESENT).
2. HashMap вычисляет hash(element).
3. Определяется бакет по индексу.
4. Проверяется наличие ключа через equals():
— Если есть: возвращается false (дубликат не добавлен).
— Если нет: создаётся Entry, возвращается true.

Сложность: O(1) в среднем.

🔎 contains(Object o) — проверка наличия

1. Вызывается map.containsKey(o).
2. HashMap ищет ключ по hash и equals().
3. Возвращается true/false.

Сложность: O(1) в среднем.

remove(Object o) — удаление

1. Вызывается map.remove(o).
2. HashMap находит и удаляет ключ.
3. Возвращается true если элемент был, иначе false.

Сложность: O(1) в среднем.

⚖️ Важные нюансы

1️⃣ Наследование и делегирование

HashSet НЕ наследует HashMap, а содержит его как поле. Все методы делегируют вызовы.

2️⃣ Null элемент

HashSet допускает один null (так как HashMap допускает null key).

3️⃣ Equals и hashCode

Элементы ДОЛЖНЫ корректно реализовывать hashCode() и equals().

4️⃣ Не потокобезопасен

Для concurrent доступа:
Collections.synchronizedSet(new HashSet<>());
ConcurrentHashMap.newKeySet();
CopyOnWriteArraySet (для read-heavy нагрузки).

5️⃣ Initial capacity и load factor

Как у HashMap, можно указать при создании. Это помогает избегать resize операций.

✔️ Полезен для

→ Быстрой проверки наличия
→ Автоматического удаления дубликатов
→ Операций над множествами. Объединение, пересечение, разность за O(n).
→ Максимальной производительности без затрат на сортировку.

🔗 Документация: JavaDoc (Java 17)

Ставьте 🔥, если хотите ещё разбор.

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👍2👏1
🚀 Просто о сложном: паттерны проектирования микросервисов

Проектирование микросервисов — это не просто «разбить монолит на части». Нужны паттерны, которые помогают сервисам надёжно общаться, масштабироваться независимо и корректно восстанавливаться при сбоях.

Разбираем ключевые паттерны микросервисной архитектуры, которые должен знать каждый инженер:

1️⃣ API Gateway

→ Единая точка входа для всех клиентов
→ Скрывает сложность внутренней архитектуры
→ Берёт на себя аутентификацию, rate limiting, маршрутизацию
→ N запросов от клиента — 1 запрос через gateway

2️⃣ Saga Pattern

→ Для распределённых транзакций (когда несколько сервисов должны завершиться успешно или откатиться)
→ Два подхода: оркестрация или хореография
→ Гарантирует консистентность данных без глобальных блокировок

3️⃣ Circuit Breaker

→ Защищает систему от медленных или падающих downstream-сервисов
→ «Размыкает цепь» при превышении порога ошибок
→ Предотвращает каскадные падения
→ Автоматически восстанавливается после стабилизации

4️⃣ Event-Driven Architecture

→ Сервисы общаются через события вместо прямых вызовов
→ Слабая связанность компонентов
→ Отличная масштабируемость
→ Идеально для real-time обновлений

5️⃣ Strangler Fig

→ Постепенная миграция монолита
→ Выносим модули один за другим
→ Маршрутизируем трафик через gateway
→ Миграция без даунтайма

6️⃣ Database per Service

→ Каждый сервис владеет своими данными
→ Слабая связанность
→ Независимые деплои
→ Избегаем bottleneck «одной большой общей БД»

💬 Какие паттерны используете часто? Делитесь опытом в комментах 👇

🔹 Курс «Алгоритмы и структуры данных»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍64🔥2
👀 Внутреннее устройство LinkedList

LinkedList — это реализация интерфейса List и Deque на основе двусвязного списка. Отличный выбор для частых вставок/удалений, но медленный доступ по индексу.

📦 Базовая структура

LinkedList состоит из узлов (Node), связанных ссылками:

public class LinkedList<E> {
transient int size = 0;
transient Node<E> first; // голова списка
transient Node<E> last; // хвост списка

private static class Node<E> {
E item;
Node<E> next; // следующий узел
Node<E> prev; // предыдущий узел
}
}


Главные особенности:

— O(1) для add/remove с концов (first/last).
— O(n) для доступа по индексу (нужен обход).
— O(1) для вставки/удаления при наличии ссылки на узел.
— Больше памяти чем ArrayList (~24 байта overhead на элемент).

🔍 Как устроено хранение

Внутренности Node

→ item: элемент
→ prev: ссылка на предыдущий узел
→ next: ссылка на следующий узел

Преимущества двусвязного списка

— Обход в обе стороны (вперёд и назад).
— Быстрое удаление узла при наличии ссылки на него.
— Динамический размер без перевыделения памяти.

⚡️ Операции добавления, удаления и доступа

add(E element) — добавление в конец

1. Создаётся новый Node: newNode = new Node<>(last, element, null).
2. Если список пустой (last == null), то first = last = newNode.
3. Иначе: last.next = newNode И last = newNode.
4. Увеличивается size.

Сложность: O(1).

add(int index, E element) — вставка по индексу

1. Проверяется range: index > size → IndexOutOfBoundsException.
2. Если index == size, вызывается addLast().
3. Иначе находится узел по индексу через node(index).
4. Создаётся новый узел и вставляется перед найденным.
5. Обновляются ссылки prev/next соседних узлов.

Сложность: O(n) — нужен поиск узла по индексу.

🔎 get(int index) — получение по индексу

1. Проверка границ: index >= size → IndexOutOfBoundsException.
2. Вызывается node(index) для поиска узла.
3. node(index) использует оптимизацию:
— Если index < size/2: обход от first вперёд.
— Иначе: обход от last назад.
4. Возвращается item найденного узла.

Сложность: O(n) — в худшем случае обход половины списка.

remove(int index) — удаление по индексу

1. Находится узел по индексу через node(index).
2. Узел отсоединяется от списка:
node.prev.next = node.next (если prev != null).
— node.next.prev = node.prev (если next != null).
3. Обновляются first/last если нужно.
4. Обнуляются ссылки в узле для GC: node.item = null; node.next = node.prev = null.

Сложность: O(n) — поиск узла O(n), удаление O(1).

addFirst(E e) / addLast(E e)

Специальные методы для работы как Deque:

list.addFirst("A");  // O(1) — добавление в начало
list.addLast("Z"); // O(1) — добавление в конец


Сложность: O(1) — прямое изменение first/last.

removeFirst() / removeLast()

list.removeFirst();  // O(1) — удаление первого
list.removeLast(); // O(1) — удаление последнего


Сложность: O(1) — прямой доступ к first/last.

⚖️ Важные нюансы

1️⃣ Реализует Deque

LinkedList может работать как двусторонняя очередь. Идеально для реализации стеков и очередей.

2️⃣ Память

Каждый узел требует ~24 байта overhead (на 64-bit JVM):
— 12 байт заголовок объекта
— 8 байт ссылка item
— 8 байт ссылка next
— 8 байт ссылка prev
— Padding до 8 байт

Для списка из 1000 элементов overhead ~24 КБ против ~4 КБ у ArrayList.

3️⃣ Нет RandomAccess

LinkedList НЕ реализует RandomAccess marker interface. Это сигнал для алгоритмов использовать итераторы вместо get(i).

//  Медленно — O(n²)
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
}

// Быстро — O(n)
for (String s : list) {
// iterator используется автоматически
}


4️⃣ ListIterator

ListIterator позволяет двунаправленный обход и модификацию.

5️⃣ Null элементы

LinkedList допускает null значения.

✔️ Полезен если

→ Частые вставки/удаления с концов.
→ Модификация при итерации.
→ Неизвестный размер с частым ростом (Нет overhead на расширение массива как у ArrayList).

🔗 Документация: JavaDoc (Java 17)

Ставьте 🔥, если хотите ещё разбор.

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍41
🔥 Устал каждый раз городить велосипед для Telegram-ботов на Spring Boot?

Новый готовый Spring Boot Starter решает именно эту боль: минимальная конфигурация, понятный pipeline обработки апдейтов, маршрутизация, обработка ошибок и простая интеграция в Spring-экосистему — всё из коробки.

Архитектура разделяет приём апдейтов (Ingress), Delivery, Interceptor, Dispatcher и Router/Handler, а также даёт готовые хуки расширения и обработки нестандартных сценариев.

🔗 Подробнее в статье

🔹 Курс «Алгоритмы и структуры данных»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥31