Библиотека джависта | Java, Spring, Maven, Hibernate
23.1K subscribers
2.26K photos
48 videos
46 files
3.22K links
Все самое полезное для Java-разработчика в одном канале.

Список наших каналов: https://xn--r1a.website/proglibrary/9197

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

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

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
🎯 Optional: используй правильно

Optional появился в Java 8, но многие до сих пор используют его неэффективно. Короткий чеклист 👇

Антипаттерны

// Плохо #1: проверка через isPresent()
if (optional.isPresent()) {
doSomething(optional.get());
}
// ✔️ Правильно:
optional.ifPresent(this::doSomething);

// Плохо #2: get() без проверки
User user = userOptional.get(); // NoSuchElementException
// ✔️ Правильно:
User user = userOptional.orElseThrow(() ->
new UserNotFoundException("User not found"));

// Плохо #3: orElse с вычислением
return optional.orElse(repository.findDefault()); // ВСЕГДА вызовется!
// ✔️ Правильно:
return optional.orElseGet(() -> repository.findDefault());


✔️ Полезные комбинации

// map + orElse
String name = userOptional
.map(User::getName)
.orElse("Anonymous");

// flatMap для вложенных Optional
Optional<String> email = userOptional
.flatMap(User::getEmail); // User::getEmail возвращает Optional<String>

// filter + ifPresent
userOptional
.filter(u -> u.getAge() > 18)
.ifPresent(this::sendAdultContent);

// or (Java 9+) - fallback к другому Optional
return cache.get(id)
.or(() -> database.find(id))
.orElseThrow();


Когда НЕ использовать

— Не используй Optional в полях класса
— Не передавай Optional как параметр метода
— Не создавай Optional для коллекций (верни пустую коллекцию)
— Не используй Optional для примитивов (есть OptionalInt, OptionalLong, OptionalDouble)

✔️ Когда использовать

— Возвращаемое значение метода, которое может отсутствовать
— Stream API operations
— Замена null в return

Ставь → 🔥, если полезно и хочешь короткие разборы других фич?

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

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥433👍3🤔2
👀 Внутреннее устройство Map.computeIfAbsent()

computeIfAbsent() — это не просто «get или put». Это атомарная операция с ленивым вычислением, которая решает классическую проблему check-then-act в многопоточном коде.

📦 Что такое computeIfAbsent()

Поведение

1. Если ключ существует и value != null → вернуть value
2. Если ключа нет или value == null → вызвать mappingFunction
3. Результат функции put в map
4. Вернуть computed value

Классический use case:

//  Старый способ — race condition!
if (!map.containsKey(key)) {
map.put(key, expensiveOperation());
}

// Новый способ — атомарно
map.computeIfAbsent(key, k -> expensiveOperation());


🔍 Упрощённый код из JDK:

public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
if (mappingFunction == null)
throw new NullPointerException();

int hash = hash(key);
Node<K,V>[] tab = table;
Node<K,V> first = tab[index];

// Поиск существующего entry
if (first != null) {
Node<K,V> e = first;
do {
if (e.hash == hash &&
Objects.equals(key, e.key)) {
V v = e.value;
if (v != null) {
return v; // Найден, не вызываем функцию!
}
}
} while ((e = e.next) != null);
}

// Ключа нет — вызов mappingFunction
V newValue = mappingFunction.apply(key);

if (newValue != null) {
putVal(hash, key, newValue, true, true);
}

return newValue;
}


📊 Performance

Benchmark: 1M операций
// Старый способ: containsKey + put
if (!map.containsKey(key)) {
map.put(key, new ArrayList<>());
}
// Time: ~45ms, 2 hash lookups

// computeIfAbsent
map.computeIfAbsent(key, k -> new ArrayList<>());
// Time: ~30ms, 1 hash lookup

computeIfAbsent() на 33% быстрее!


Делайте

— Используйте для lazy initialization
— Используйте ConcurrentHashMap для thread-safety
— Держите mappingFunction быстрым и простым

Не делайте

— Не вызывайте computeIfAbsent рекурсивно на том же ключе
— Не модифицируйте map внутри mappingFunction
— Не возвращайте null если хотите кэшировать отсутствие
— Не используйте для побочных эффектов (только для вычисления value)

🔗 Документация

Ставьте 🔥, если интересны другие Map методы!

Бонусы для подписчиков:
Скидка 40% на все курсы Академии
Розыгрыш Apple MacBook
Бесплатный тест на знание математики

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥104👍4👏1
💬 Обратная связь

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

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

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

#DevLife
Please open Telegram to view this post
VIEW IN TELEGRAM
20👍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
🔥19👍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
👍65🔥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
👀 Внутреннее устройство List.of()

List.of() появился в Java 9 и произвёл революцию в создании коллекций. Но под капотом это не просто обёртка над ArrayList — это целая иерархия оптимизированных классов.

📦 Сигнатура и варианты


Java предоставляет 12 перегруженных методов:
// Специализированные версии для 0-10 элементов
static <E> List<E> of()
static <E> List<E> of(E e1)
static <E> List<E> of(E e1, E e2)
// ... до 10 параметров
static <E> List<E> of(E e1, E e2, ..., E e10)
// Varargs для 11+ элементов
static <E> List<E> of(E... elements)


Зачем столько перегрузок?

— Избежать overhead создания varargs массива
— Каждая перегрузка возвращает оптимизированный класс
— Специализация для частых случаев (1-2 элемента)

🔍 Внутренняя реализация

Разные классы для разного размера:

// 0 элементов — singleton
static <E> List<E> of() {
return ImmutableCollections.EMPTY_LIST; // Переиспользуется!
}

// 1 элемент — List12 (List of 1 or 2)
static <E> List<E> of(E e1) {
return new ImmutableCollections.List12<>(e1);
}

// 2 элемента — тот же List12
static <E> List<E> of(E e1, E e2) {
return new ImmutableCollections.List12<>(e1, e2);
}

// 3+ элементов — ListN
static <E> List<E> of(E e1, E e2, E e3) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3);
}


Структура List12 (для 1-2 элементов):

static final class List12<E> extends AbstractImmutableList<E> {
private final E e0; // Всегда присутствует
private final E e1; // null если один элемент

List12(E e0) {
this.e0 = Objects.requireNonNull(e0);
this.e1 = null;
}

List12(E e0, E e1) {
this.e0 = Objects.requireNonNull(e0);
this.e1 = Objects.requireNonNull(e1);
}

public int size() {
return e1 != null ? 2 : 1;
}

public E get(int index) {
if (index == 0) return e0;
else if (index == 1 && e1 != null) return e1;
throw new IndexOutOfBoundsException();
}
}


Структура ListN (для 3+ элементов):

static final class ListN<E> extends AbstractImmutableList<E> {
private final E[] elements; // Хранит все элементы

ListN(E... elements) {
// Копирование + null-check каждого элемента
this.elements = elements.clone();
for (E e : this.elements) {
Objects.requireNonNull(e);
}
}

public int size() {
return elements.length;
}

public E get(int index) {
return elements[index];
}
}


⚠️ Подводные камни

1. Null элементы запрещены.
2. Varargs создаёт массив.
3. Не Serializable.
4. SubList тоже immutable.

📊 Performance бенчмарк

Создание списка из 3 элементов:

List.of("A", "B", "C"):                ~5 ns
Arrays.asList("A", "B", "C"): ~8 ns
new ArrayList<>(Arrays.asList(...)): ~25 ns

List.of() в 5 раз быстрее ArrayList!


Memory footprint:

List.of(1, 2, 3):           48 байт
Arrays.asList(1, 2, 3): ~72 байт (backing array + wrapper)
new ArrayList<>(3 items): ~112 байт (default capacity 10)

List.of() в 2.3 раза компактнее!


🔗 Документация

══════ Навигация ══════
ВакансииЗадачиСобесы

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍164🔥1
🔍 Задачка с реального собеса

Что будет результатом кода? Смотри правильный ответ.

══════ Навигация ══════
ВакансииЗадачиСобесы

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

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

PriorityQueue — это реализация интерфейса Queue на основе двоичной кучи. Элементы всегда упорядочены по приоритету, минимальный элемент всегда в голове.

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

PriorityQueue использует min-heap реализацию:

public class PriorityQueue<E> {
transient Object[] queue; // массив-куча
private int size = 0;
private final Comparator<? super E> comparator;

private static final int DEFAULT_INITIAL_CAPACITY = 11;
}


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

→ O(log n) для add и poll (вставка/удаление с перестройкой).
→ O(1) для peek (доступ к минимуму).
→ O(n) для remove(Object) и contains.
→ Не гарантирует полную сортировку, только min в голове.
→ Не потокобезопасна (для concurrent используйте PriorityBlockingQueue).

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

Binary Heap — complete binary tree в массиве

PriorityQueue с элементами: [2, 5, 8, 9, 12, 10, 15]

Куча (min-heap):
2
/ \
5 8
/ \ / \
9 12 10 15

Массив queue[]:
[2, 5, 8, 9, 12, 10, 15]
0 1 2 3 4 5 6

Индексация:
- Родитель узла i: (i - 1) / 2
- Левый ребёнок узла i: 2 * i + 1
- Правый ребёнок узла i: 2 * i + 2


Свойство min-heap:

Каждый родитель ≤ своих детей. Это НЕ полная сортировка — только гарантия минимума в корне.

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

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

1. Элемент добавляется в конец массива: queue[size++] = element.
2. Вызывается siftUp(size-1) для восстановления heap property:
— Элемент "всплывает" вверх, пока не станет больше родителя.
— Сравнения через Comparator или Comparable.

Алгоритм siftUp:

void siftUp(int k) {
E x = queue[k];
while (k > 0) {
int parent = (k - 1) >>> 1; // деление на 2
E e = queue[parent];
if (compare(x, e) >= 0) break;
queue[k] = e; // родитель опускается
k = parent; // поднимаемся выше
}
queue[k] = x; // вставляем элемент
}


Сложность: O(log n) — высота дерева.

poll() — удаление минимального элемента

1. Сохраняется минимум: E result = queue[0].
2. Последний элемент перемещается в корень: queue[0] = queue[--size].
3. Вызывается siftDown(0) для восстановления heap:
— Элемент "тонет" вниз, меняясь с меньшим ребёнком.

Алгоритм siftDown:

void siftDown(int k) {
E x = queue[k];
int half = size >>> 1; // size / 2
while (k < half) {
int child = (k << 1) + 1; // левый ребёнок
E c = queue[child];
int right = child + 1;
if (right < size && compare(c, queue[right]) > 0) {
child = right; // правый меньше
c = queue[child];
}
if (compare(x, c) <= 0) break;
queue[k] = c; // ребёнок поднимается
k = child; // опускаемся ниже
}
queue[k] = x;
}


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

🔎 peek() — доступ к минимуму

E peek() {
return (size == 0) ? null : (E) queue[0];
}


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

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


1. Линейный поиск по массиву queue[].
2. Сравнение через equals().

Сложность: O(n) — требуется обход массива.

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

1. Линейный поиск элемента: O(n).
2. Удаление найденного:
— Перемещение последнего элемента на место удаляемого.
— siftDown() или siftUp() для восстановления heap.

Сложность: O(n) поиск + O(log n) восстановление = O(n).

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

1️⃣ Comparator vs Comparable

Элементы должны быть сравниваемыми. Без Comparator/Comparable → ClassCastException.

2️⃣ НЕ полностью отсортирована

Heap гарантирует только минимум в корне.

3️⃣ Iterator не гарантирует порядок

Для sorted iteration используйте poll() в цикле:

4️⃣ Null элементы НЕ допускаются

Подходит


— Нужен доступ к минимуму/максимуму
— Динамическая сортировка
— Алгоритмы с приоритетами
— Top K проблема

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

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

══════ Навигация ══════
ВакансииЗадачиСобесы

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍31
📈 Big-O ≠ производительность

Часто выбор коллекции ограничивается только таблицей сложностей и на этом всё.

Но реальный кейс сложнее: средняя сложность ≠ реальная скорость в продакшне. JVM, кэш процессора, GC и паттерны доступа могут радикально поменять картину.

🔑 Главная мысль

Выбирайте коллекцию под сценарий использования, а не “по самой быстрой ячейке в таблице”.

1️⃣ ArrayList — быстр в чтение, но не во вставке

ArrayList хранит элементы в массиве → локальность памяти + CPU кэш → итерации летят.
Вставка в середину за O(n), но при небольших списках разница с LinkedList исчезающе мала.

🔧 Паттерн использования:

— 90% чтение, редкие вставки → идеально.
— Если заранее известно примерное кол-во элементов → задайте initialCapacity, иначе ArrayList будет несколько раз пересоздавать массив (copy O(n) на каждом росте).

📌 Факт:

В бенчмарках JMH даже при вставке в середину ArrayList часто быстрее LinkedList просто потому, что LinkedList платит за “pointer chasing” (скачки по памяти, cache-miss).

2️⃣ LinkedList — звучит круто, но редко нужен

Да, вставка/удаление в начало или конец за O(1).
Но get(i) = O(n), и каждый шаг = новый объект, новая ссылка → нагрузка на GC.

🔧 Паттерн использования:

— Когда нужна двусторонняя очередь с частыми удалениями/добавлениями в начало и конец.
— Во всех остальных случаях лучше ArrayDeque, он без лишних объектов и быстрее почти всегда.

📌 Факт:

LinkedList ест больше памяти: на каждый элемент два указателя + объект-узел.

3️⃣ HashMap / HashSet — быстрые, пока не наступил resize

HashMap даёт O(1) доступ при хорошем hashCode().

Но:
— Если хэши “плохие” → коллизии → O(log n)
— При достижении load factor 0.75 → resize → перераспределение всех бакетов (дорогая операция).

🔧 Паттерн использования:

— Когда нужен быстрый поиск по ключу без сохранения порядка или когда важно хранить уникальные элементы или строить словари/кэши по ключу.
— Если знаете примерное кол-во элементов → сразу задайте кол-во элементов в конструкторе new HashMap<>(N).

📌 Факт:

Начиная с Java 8 при коллизии, когда LinkedList становится длинным (по умолчанию ≥ 8 элементов) → список превращается в красно-чёрное дерево.

4️⃣ TreeMap / TreeSet — порядок стоит денег

Дают O(log n) доступ и всегда хранят ключи отсортированными.
Но если сортировка нужна редко, дешевле собрать HashMap и вызвать sorted() на стриме.

🔧 Паттерн использования:

— Когда важно поддерживать сортировку на каждой операции (напр. Top-N задач в приоритетной очереди).
— Не храните mutable-ключи, т.к. можно “потерять” элемент при изменении поля, участвующего в compareTo.

📌 Факт:

TreeMap хранит узлы с балансировкой (красно-чёрное дерево) → накладные расходы на память + сравнения ключей.

5️⃣ LinkedHashMap — скрытый герой для кэшей

LinkedHashMap поддерживает порядок вставки или порядок доступа (accessOrder=true).
Можно сделать LRU-кэш, переопределив removeEldestEntry.

🔧 Паттерн использования:

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

📌 Факт:

Каждый get() в режиме accessOrder вызывает перестановку в двусвязном списке → небольшие накладные расходы.

══════ Навигация ══════
ВакансииЗадачиСобесы

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

#CoreJava #лучшее2025
Please open Telegram to view this post
VIEW IN TELEGRAM
👍163🔥2👏1
⚙️ Как работает Garbage Collector

Механизм сборки мусора в JVM — это не просто “магия, которая чистит память”, а сложная система, работающая по поколениям, фазам и стратегиям.

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

🔹 Архитектура: как устроена куча (Heap)

Куча памяти делится на поколения:
Heap
├── Young Generation
│ ├── Eden Space
│ └── Survivor Spaces (S0, S1)
└── Old Generation (Tenured)


Eden — вновь созданные объекты.
Survivor — те, кто “выжил” после первой сборки.
Old Gen — объекты, пережившие несколько сборок, считаются “долгоживущими”.

Дополнительно есть Metaspace (с Java 8), где хранятся данные о классах.

🔹 Алгоритм работы GC: по фазам

1. Mark

GC начинает с “корневых” ссылок (стек, глобальные переменные) и помечает все достижимые объекты.

2. Sweep
Удаляются все немаркированные объекты — они считаются “мертвыми”.

3. Compact (в некоторых GC)
Уплотнение памяти: “живые” объекты перемещаются ближе друг к другу, чтобы избежать фрагментации.

🔹 Типы сборок

1. Minor GC
Запускается при заполнении Eden. Очищаются только молодые поколения. Быстро, но может происходить часто.

2. Major GC / Full GC
Включает Old Gen и Metaspace. Дорогая операция, может “заморозить” все потоки (stop-the-world pause).

🔹 Типы сборщиков и их принципы

— Serial GC: однопоточная сборка. Просто и медленно.
— Parallel GC: многопоточная сборка всех поколений. Высокая пропускная способность.
— G1 GC: делит кучу на регионы, параллельно собирает “Region Set”. Поддерживает предсказуемые паузы.
— ZGC: целиком конкурентный сборщик. Работает с огромными кучами (до терабайта), паузы <10 мс.
— Shenandoah: минимальные паузы за счёт почти полной конкуренции с пользовательскими потоками.

🔹 Как GC определяет, что объект мёртв?

GC не использует reference count. Он строит граф достижимости:
1. Начинает с “корней” (GC roots)
2. Если оттуда нельзя добраться до объекта — он считается мусором
3. Это позволяет избежать утечек при циклических ссылках

🔹 Советы по оптимизации

— Избегайте долгоживущих ссылок (static, ThreadLocal) без необходимости
— Используйте WeakReference, если хотите избежать удержания объекта GC
— Кэшируйте объекты осознанно — утечка через Map может быть незаметной
— Задавайте лимиты памяти (-Xms512m -Xmx1024m)

💬 Ловили OutOfMemoryError в проде когда-нибудь?

══════ Навигация ══════
ВакансииЗадачиСобесы

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

#CoreJava #лучшее2025
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥42👏2
💼⌛️ ТОП-5 причин, почему программист не может долго найти работу

Почему некоторые разработчики остаются "между работами" месяцы?

Не всегда дело в нехватке вакансий или «рынок просел». Часто дело в подходе к поиску проекта. Вроде бы есть опыт, стек, даже pet-проекты, но офферов всё нет.

Часто корень проблемы — неумение продать себя правильно. Отказ выполнять тестовые задания, считая их ненужными или обидными. Кроме того, нежелание рассматривать стажировки как стартовую площадку для получения опыта и расширения профессиональных связей также может замедлить процесс трудоустройства. И это далеко не все возможные причины.

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

══════ Навигация ══════
ВакансииЗадачиСобесы

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

#CoreJava #лучшее2025
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5😁3🔥2👏1
📈 Как «ленивая разработка» захватывает IT-рынок

Пока мы выстраиваем архитектуру, пишем тесты и спорим о лучших практиках, рынок всё активнее обживают те, кто вообще не пишет код. Low-code и no-code решения не просто живы — они становятся нормой для бизнеса.

Порог входа минимальный, скорость разработки — бешеная, а заказчику всё равно, написано ли это на Java или накликано в визуальном редакторе. Вопрос: как долго останется актуальной классическая разработка?

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

══════ Навигация ══════
ВакансииЗадачиСобесы

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

#CoreJava #лучшее2025
Please open Telegram to view this post
VIEW IN TELEGRAM
😁11🥱3👍1🔥1🤔1
⚡️ Параллельные стримы: ускорение или нет?

Java предоставляет мощный инструмент для обработки данных — параллельные стримы. Они позволяют автоматически распределять вычисления по нескольким потокам, но их эффективность зависит от множества факторов.

Добавление parallelStream() бездумно — это не "оптимизация", а лотерея с шансом на баги и падение.

Когда не использовать

— При небольшом наборе данных (<10 000 элементов) затраты на управление потоками могут превышать прирост скорости.
— Операции sorted(), distinct() или limit() требуют полного знания данных, что снижает эффективность параллельного выполнения.
— Вложенные parallelStream() в CompletableFuture или ExecutorService могут привести к конкуренции за ресурсы и неожиданному падению производительности.

✔️ Когда использовать

— Обработка больших объёмов данных (100 000+ элементов).
— Операции независимы и ресурсоёмки, например, сложные вычисления, парсинг файлов, загрузка данных из сети.

🔍 Важная особенность

parallelStream() использует ForkJoinPool.commonPool(). Если есть другие задачи, использующие этот же пул, они могут начать конкурировать за потоки, замедляя всё приложение.

💬 Делитесь в комментах интересными кейсами

══════ Навигация ══════
ВакансииЗадачиСобесы

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

#CoreJava #лучшее2025
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥21👏1
🖥 Когда Kafka транзакции действительно нужны

Kafka транзакции не панацея, а узкоспециализированный инструмент.

🔹 Что гарантируют Kafka транзакции:

— Атомарную публикацию в несколько топиков (все сообщения видны или ни одно).
— Связывание read → process → write в одну операцию для консьюмеров-продюсеров.

🔹 Что НЕ гарантируют:


— Откат изменений во внешних системах (БД, API, email).
— Защиту от дубликатов, если downstream-сервисы не настроены правильно.

🔹 Два реальных сценария использования транзакций:

1️⃣ Публикация в несколько топиков без возможности дедупликации

Отправка события обработки заказа и событие в audit-топик с временной меткой. Если сервис упадёт между отправками, то при повторе время не совпадёт — история будет некорректной.

Транзакция гарантирует, что оба сообщения либо станут видимы консьюмерам одновременно, либо ни одно из них не будет доставлено.

2️⃣ Схема read → process → write без дедупликации

Если сервис упадёт после отправки сообщений, но до коммита оффсета, downstream-сервисы увидят дубликаты.

Транзакции связывают отправку и коммит в одну атомарную операцию.

💡 Ключевой вывод: большинство систем прекрасно работают без транзакций, используя дедупликацию и идемпотентность.

Транзакции добавляют накладные расходы и подходят только для специфических кейсов.

══════ Навигация ══════
ВакансииЗадачиСобесы

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍63🔥1👏1