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

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

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

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

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
👀 Внутреннее устройство LinkedHashSet

LinkedHashSet — это реализация интерфейса Set из пакета java.util, которая сохраняет порядок вставки элементов. Это гибрид между HashSet (быстрый поиск) и списком (предсказуемый порядок итерации).

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


LinkedHashSet — это тонкая обёртка над LinkedHashMap. Внутри:

— Все элементы хранятся как ключи в LinkedHashMap.

— Значения — константа PRESENT (заглушка Object).

— Порядок поддерживается через двусвязный список узлов.

Главная особенность:

— O(1) для add, remove, contains (как у HashSet).

— Предсказуемый порядок итерации (порядок вставки).

— Немного больше памяти, чем HashSet (~25% overhead на связи).

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

LinkedHashSet полностью делегирует работу LinkedHashMap, а тот устроен так:
Entry<K,V> — узел хранения:
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before; // предыдущий в порядке вставки
Entry<K,V> after; // следующий в порядке вставки
}
```

### **Двойная структура:**

1. **Хэш-таблица** (массив бакетов):
- Быстрый доступ по хэшу → O(1).
- Разрешение коллизий через цепочки/деревья.

2. **Двусвязный список**:
- Связывает все элементы в порядке добавления.
- Голова списка: `head` (первый добавленный).
- Хвост списка: `tail` (последний добавленный).

**Визуализация:**
```
Хэш-таблица:
Bucket[0]: null
Bucket[1]: Entry("B") ----→ Entry("F")
Bucket[2]: Entry("A")
Bucket[3]: Entry("C")

Двусвязный список (порядок вставки):
head → Entry("A") ⇄ Entry("B") ⇄ Entry("C") ⇄ Entry("F") ← tail


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

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

1. Вычисляется хэш элемента: hash(e).
2. Определяется индекс бакета: index = hash & (n-1).
3. Проверяется наличие элемента в бакете:
— если есть → возвращается false (дубликат).
— если нет → создаётся новый Entry.
4. Новый Entry:
— добавляется в бакет хэш-таблицы.
— связывается с tail в двусвязном списке.
— обновляется tail = newEntry.
5. При необходимости таблица расширяется (load factor > 0.75).

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

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

1. Вычисляется хэш объекта.
2. Проверяется соответствующий бакет.
3. Сравнивается через equals().
4. Двусвязный список НЕ используется для поиска.

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

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

1. Находится Entry в хэш-таблице по хэшу.
2. Узел удаляется из бакета.
3. Узел отсоединяется от двусвязного списка.
4. Обновляются ссылки head/tail при необходимости.

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

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

1. Наследование от HashSet

Наследует поведение HashSet, но меняет внутреннюю реализацию. Конструкторы создают LinkedHashMap вместо HashMap.

2. Null элементы

Один null может быть добавлен (как в HashSet).

3. Не потокобезопасен

Для многопоточного доступа требуется внешняя синхронизация. Альтернатива: CopyOnWriteArraySet (но без хэш-таблицы).

4. Equals и hashCode

Сравнивает содержимое, игнорируя порядок:

5. Capacity и Load Factor

Начальные значения: Capacity 100, load factor 0.75

Начальная ёмкость должна учитывать ожидаемый размер.
При достижении threshold (capacity × load factor) происходит resize.

Не использовать если

1. Порядок не важен

Используйте HashSet — проще и немного быстрее (меньше overhead).

2. Нужна сортировка

Используйте TreeSet — автоматическая сортировка по Comparator/Comparable.

3. Многопоточный доступ

Используйте ConcurrentHashMap.newKeySet() или CopyOnWriteArraySet. Или оборачивайте: Collections.synchronizedSet().

4. Критична минимизация памяти:

HashSet использует меньше памяти (~20% экономии).

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

Ставьте 🔥, если хотите разбор TreeSet!

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍41🎉1
⚙️ Byte Buddy

Byte Buddy — это библиотека для runtime генерации и модификации байт-кода Java. В отличие от классических ASM или Javassist, она предлагает type-safe API и не требует глубоких знаний JVM internals.

📌 Что умеет Byte Buddy

— Создаёт классы и прокси на лету без написания байт-кода вручную
— Перехватывает методы, добавляет логирование, метрики или трейсинг без изменения исходного кода
— Работает как Java-агент для инструментации существующих классов (например, для APM-систем)
— Интегрируется с Mockito, Hibernate и другими фреймворками под капотом
— Поддерживает все современные Java-фичи (records, sealed classes, pattern matching)

🧠 Особенно полезно для

Создания динамических прокси с кастомным поведением, написания собственных фреймворков или библиотек с AOP-логикой, инструментации приложений для мониторинга (как делают Datadog, New Relic), тестирования с подменой реализаций классов.

Byte Buddy даёт мощь низкоуровневой манипуляции классами, но с человеческим API. Если вы когда-то смотрели на ASM и думали "это слишком сложно" — попробуйте Byte Buddy.

🔗 Byte Buddy на GitHub

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍112🔥1🤔1
Разница StackOverflowError и OutOfMemoryError?

— StackOverflowError

Возникает когда переполняется стек вызовов конкретного потока. Обычно из-за бесконечной или слишком глубокой рекурсии.

— OutOfMemoryError

Возникает когда JVM не может выделить память в куче (heap) для новых объектов. Это проблема всего приложения, а не отдельного потока.

🐸 Библиотека собеса по Java

#jvm
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥2👏1
🔍 Просто о сложном: Sealed Classes

В Java 17 появились Sealed Classes — механизм явного контроля иерархии наследования. Теперь можно точно указать, какие классы могут наследоваться от вашего типа.

По сути, это золотая середина между публичными классами (наследовать может кто угодно) и final классами (наследовать нельзя вообще). Вы сами решаете, кто входит в "белый список" наследников.

🔹 Зачем они нужны

Обычное наследование имеет проблемы:

— невозможно гарантировать закрытость иерархии (кто-то может добавить свой подтип);
— компилятор не может проверить, что все случаи покрыты (switch требует default, даже если вы обработали все варианты);
— сложно моделировать ADT (Algebraic Data Types) из функционального программирования.

Sealed классы решают эти проблемы: компилятор знает все возможные подтипы и может проверить полноту обработки в pattern matching.

🔹 Ключевые моменты

▪️ sealed — ключевое слово для объявления запечатанного класса/интерфейса.
▪️ permits — явное перечисление разрешённых наследников.
▪️ Наследники должны быть: final, sealed, или non-sealed.
▪️ Если наследники в том же файле, permits можно опустить.
▪️ Отлично работает с pattern matching и switch expressions.

public sealed interface Result<T> 
permits Success, Failure {
}

public final record Success<T>(T value)
implements Result<T> {}

public final record Failure<T>(String error)
implements Result<T> {}


🔹 Под капотом


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

Pattern matching с sealed types позволяет компилятору проверить полноту покрытия без default ветки:
return switch(result) {
case Success(var value) -> process(value);
case Failure(var error) -> handleError(error);
};


🔹 Подводные камни

— Обратная совместимость
Если вы сделали класс sealed в новой версии библиотеки, старый код с кастомными наследниками перестанет компилироваться.

— Видимость подклассов
Все наследники должны быть доступны sealed классу на момент компиляции. Нельзя добавить подкласс из другого модуля или jar.

— Сериализация
При десериализации sealed иерархии нужна осторожность? можно получить подделанный подтип. Используйте validation или sealed интерфейсы с records.

— non-sealed подклассы
Если сделать наследника non-sealed, он открывает дыру в иерархии и от него можно наследоваться кому угодно. Используйте осторожно.

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

— Моделирование состояний (State machines, FSM).
— Result/Either типы для обработки ошибок без exceptions.
— Domain-driven design с явными типами (Payment может быть Card, Cash, Crypto).
— Pattern matching в бизнес-логике с гарантией полноты.
— API, где важно контролировать расширяемость.

Не подходит:

— Публичные библиотеки с plugin-архитектурой.
— Когда нужна расширяемость от пользователей.
— Legacy код с активным использованием наследования.
— Простые entity/DTO классы без полиморфизма.

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍43👏1
Сохраняйте шпаргалку по коллекциям

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1👏1
🔍 PostgreSQL + Spring Boot

Забудьте про application.properties с хардкодом пароля. Подключение PostgreSQL к Spring Boot — это не только JDBC URL, но и пулы соединений, миграции схемы, мониторинг и graceful shutdown в production.

🧩 Вместо того чтобы копировать конфиг из StackOverflow, вы настраиваете полноценный слой персистентности: HikariCP для connection pooling, Flyway/Liquibase для версионирования схемы, Spring Data JPA для репозиториев, и health checks для Kubernetes.

📝 Промпт:

Generate a production-ready Spring Boot 3 + PostgreSQL integration with enterprise-grade configuration:

— Configure PostgreSQL datasource with HikariCP connection pool: pool size optimization, connection timeout, leak detection, and connection validation query.
— Set up Spring Data JPA with Hibernate: dialect configuration, DDL auto strategy, batch processing, second-level cache (EhCache/Redis).
— Implement database migration strategy using Flyway: versioned migrations, repeatable scripts, baseline configuration, and rollback procedures.
— Configure multiple datasources (primary + read replicas) with @Primary and @Qualifier annotations for load distribution.
— Add connection pool monitoring with Micrometer metrics: active connections, idle connections, pending threads, connection acquisition time.
— Implement database health checks for Spring Boot Actuator: connection validation, query timeout, custom health indicators.
— Configure transaction management: isolation levels, propagation strategies, read-only optimization, and timeout configuration.
— Set up connection pool resilience: retry logic with exponential backoff, circuit breaker pattern (Resilience4j), and fallback strategies.
— Add database credentials management: Spring Cloud Config integration, HashiCorp Vault support, or AWS Secrets Manager.
— Configure SSL/TLS connection with certificate validation for secure database communication.
— Implement audit logging: track query execution time, slow query detection, connection pool exhaustion alerts.
— Add database-specific optimizations: fetch size tuning, batch insert/update configuration, native query hints.
— Provide Docker Compose setup with PostgreSQL 16, pgAdmin, and application container with proper networking.
— Include integration tests with Testcontainers: schema validation, repository testing, transaction rollback verification.
— Add example entities with proper JPA annotations: @Entity, @Table, indexes, constraints, relationships (OneToMany, ManyToMany).
— Configure application profiles: dev (H2 in-memory), staging (PostgreSQL), prod (PostgreSQL with replication).

Deliverables:
— application.yml with environment-specific profiles
— build.gradle/pom.xml with all dependencies
DbConfig.java with datasource and JPA configuration
— V1__init_schema.sql Flyway migration
— Sample entity, repository, and service layer
— Integration test with Testcontainers
— docker-compose.yml for local development
— README with connection troubleshooting guide


💡 Расширения:

— добавьте pgvector для векторного поиска;
— настройте connection pool dashboard в Grafana: throughput, latency percentiles (p50/p95/p99), error rate;
— реализуйте database sharding для горизонтального масштабирования;
— интегрируйте pg_stat_statements для анализа медленных запросов в runtime.

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍31💯1
🆕 Java Digest: AI-код, API-архитектура и код-ревью

Топ-3 статьи о Java и смежных технологиях за неделю по версии нашего канала.

1️⃣ Почему AI-ассистент пишет «вырвиглазный» код

Разбор главной проблемы AI-помощников в коде — узкого контекста при RAG-подходе. Автор предлагает радикальное решение: скармливать Gemini 2.5 Pro весь проект целиком через code2prompt.

Интересный подход для стратегических задач вроде проектирования крупных фич, но важно помнить про NDA и безопасность. Метод только для pet-проектов и open source.

2️⃣ От REST к gRPC и GraphQL

Глубокое сравнение трёх подходов к построению API с практическими кейсами. Разбор гибридной архитектуры: GraphQL для фронтенда (гибкость + over/under-fetching), gRPC для микросервисов (скорость + бинарный protobuf).

3️⃣ Ошибки при код-ревью

Production-опыт о том, как правильно проводить ревью: смотреть не только на diff, ограничиваться 5-6 комментариями вместо сотни, различать личные предпочтения и реальные проблемы.

Ключевой месседж: большинство PR должны получать Approve, а не Request Changes. В конце — практичный чеклист из 14 пунктов для систематизации процесса.

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

#News
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥21🥱1💯1
🎁 И мозг прокачать, и макбук утащить!

Proglib.academy разыгрывает MacBook Pro 14 (M3 Pro, 36 GB, 1 TB SSD) 💻

Условия:

1️⃣ Покупаешь любой курс Proglib до 15 ноября.
2️⃣ Проходишь минимум 2 учебные недели (можно осилить за два вечера).
3️⃣ Пишешь куратору в чат своего курса: #розыгрыш.

Что за курсы?

Математика для Data Science (6 месяцев боли и просветления).
Основы Python, ML, алгоритмы, AI-агенты и даже курс для тех, кто в IT, но не кодит.

👉 Участвовать в розыгрыше
1
✔️ Java-тест: Безопасное извлечение значений из Map

Напишите метод для production-кода 👇

📦 Задание

Реализуйте метод для безопасного получения значения из вложенной Map с default-значением:
public class MapUtils {

public static <T> T getNestedValue(Map<String, Object> map,
String path,
T defaultValue) {
// Ваша реализация
}
}


Требования

— Путь задается через точку: "user.address.city"
— Если любой уровень вложенности null или отсутствует → вернуть defaultValue
— Обработать пустой путь → вернуть defaultValue
— Обработать null в параметрах → вернуть defaultValue

Примеры использования

Map<String, Object> data = Map.of(
"user", Map.of(
"name", "John",
"age", 30,
"address", Map.of(
"city", "Moscow"
)
)
);

String city = getNestedValue(data, "user.address.city", "Unknown");
// → "Moscow"

Integer age = getNestedValue(data, "user.age", 0);
// → 30

String country = getNestedValue(data, "user.address.country", "Russia");
// → "Russia" (не найдено)


Ставьте → 🔥, если нравится формат. Если нет → 🤔

💬 Решения под спойлер. Сравним, какое будет лучше.

🐸 Библиотека собеса по Java

#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5🤔21👍1