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

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

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

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

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
Как между собой связаны Iterable, Iterator и цикл for-each?

Iterable — это интерфейс с единственным методом iterator(), который возвращает объект Iterator. Любой класс, реализующий Iterable, может использоваться в цикле for-each.

Iterator — это интерфейс для последовательного обхода элементов коллекции. Содержит методы hasNext(), next() и remove().

Цикл for-each — это синтаксический сахар. Компилятор автоматически преобразует его в вызов iterator() и работу с Iterator.

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

#core
Please open Telegram to view this post
VIEW IN TELEGRAM
👍133🔥1🤔1
🎁 Конкурс от Proglib Academy!

Кстати, если кто-то ещё не в курсе — у нас тут раздают MacBook Pro 14.
Да-да, не шутка, настоящий, железный, с M3 Pro

Но! Чтобы успеть пройти 2 недели обучения к 15 ноября, курс нужно взять до конца октября — и сейчас на всё скидка 40%.

Чтобы поучаствовать, нужно:

1️⃣ Покупаешь любой курс до конца октября;
2️⃣ Проходишь 2 недели обучения к 15 ноября;
3️⃣ Написать куратору в чат #розыгрыш.

До 15 ноября, потом всё — поезд (и макбук) уйдёт.

👉 Участвовать в розыгрыше
Сохраняйте шпаргалку по командам git

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍123🔥2
🔍 Просто о сложном: Virtual Threads

В Java 21 появились Virtual Threads — легковесные потоки, которые позволяют писать синхронный код с производительностью асинхронного.

По сути, это JVM-управляемые потоки (а не OS-потоки), которых можно создавать миллионы без значительных затрат памяти. Они решают главную проблему масштабирования традиционных потоков.

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

Классические Platform Threads имеют проблемы:

— дорогие в создании (~1MB стека на поток);
— ограничены числом (~тысячи потоков максимум);
— при блокировке (IO, sleep) поток простаивает, занимая ресурсы.

Virtual Threads весят ~1KB, создаются мгновенно, и при блокировке платформенного потока освобождается для других задач. Это позволяет обрабатывать миллионы конкурентных запросов.

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

▪️ Thread.ofVirtual() — создание билдера для virtual thread.
▪️ Thread.startVirtualThread() — быстрый старт задачи.
▪️ Executors.newVirtualThreadPerTaskExecutor() — пул для каждой задачи создаёт новый VT.
▪️ Автоматическое отсоединение от платформенного потока при блокировке (IO, sleep, wait, park).
▪️ Присоединение к платформенным потокам из ForkJoinPool.

🔹 Под капотом

Когда виртуальный поток блокируется (например, на IO), он "отцепляется" от платформенного потока. Освободившийся платформенный поток берёт другой готовый виртуальный поток. Когда операция завершается, виртуальный поток "подцепляется" обратно к доступному платформенному потоку.

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

— Pinning (закрепление)

Virtual thread может "застрять" на платформенном потоке при:
• Synchronized блоках
• Нативных методах (JNI)

В таких случаях carrier thread блокируется вместе с virtual thread. Решение: использовать ReentrantLock вместо synchronized.

— ThreadLocal может быть опасен

Миллионы virtual threads с ThreadLocal приведут к огромному потреблению памяти. Используйте ScopedValue (preview feature в Java 21+).

— Не подходит для CPU-bound задач

Virtual threads оптимизированы для IO-bound операций. Для вычислений лучше параллельные стримы или ForkJoinPool.

— Мониторинг
Стандартные инструменты мониторинга потоков могут показывать некорректные данные — они заточены под platform threads.

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

— Высоконагруженные web-серверы с множеством конкурентных запросов.
— Микросервисы с большим количеством внешних вызовов (HTTP, БД).
— Когда нужна простота синхронного кода без сложности реактивного.
— Замена больших thread pools для IO-операций.

Не подходит:

— CPU-intensive вычисления (сортировки, криптография).
— Код с большим количеством synchronized блоков (pinning).
— Легаси-код с активным использованием ThreadLocal.

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥52💯1
Please open Telegram to view this post
VIEW IN TELEGRAM
😁24🔥4💯4❤‍🔥2👍1
Код-ревью Java-проекта прямо в браузере, как в IDE? Похоже на правду! 👀

Джависты, внимание. Платформа SourceCraft выкатила кодонавигацию для Java, которая работает прямо в веб-интерфейсе. Можно открыть любой класс и одним кликом перейти к декларации метода, увидеть все его использования (Find Usages), посмотреть историю изменений по коммитам. 🔥

Это дико удобно при ревью или анализе больших проектов — не нужно клонировать репо и настраивать IDE. Всё работает из браузера. Для команд это реальная экономия времени. По сути, полноценное code-review пространство со встроенными IDE-инструментами.
👍9🔥1
🎃 Хэллоуин в Proglib Academy: скидки, призы и... немного паники

Сегодня 31 октября, и это не просто время тыкв и призраков, это ПОСЛЕДНИЙ ДЕНЬ, когда ты можешь выиграть макбук!

→ Купи любой курс со скидкой 40% 💸
→ Начни обучение, чтобы пройти 2 недели к 15 ноября 🎓
→ Напиши куратору #розыгрыш ✍️

Всё! Теперь ты в игре.

👉 Сейчас или никогда!
😁1
🎭 LangChain4j + Spring Boot

Забудьте про «LLM в лоб». LangChain4j — это промышленный слой интеграции AI с Java-приложениями. Он превращает LLM в часть архитектуры, а не игрушку для экспериментов.

🧩 Вместо того чтобы вызывать модель напрямую, вы описываете цепочку: загрузка данных → векторизация → поиск → рассуждение → ответ. Всё это живёт в Spring-экосистеме, с DI, профилями, бинами и транзакциями.

📝 Промпт:

Generate a production-ready Spring Boot 3 + LangChain4j integration for enterprise RAG and AI agent workflows:

— Configure LangChain4j client with OpenAI or Ollama connector, API-key management, and retry policy.
— Implement document ingestion pipeline: PDF, Markdown, and web sources → chunking → embedding store (Postgres + pgvector).
— Add Retrieval-Augmented Generation chain with context ranking, token budget control, and caching.
— Implement conversational memory (message buffer + summary).
— Create multi-agent setup: retriever → reasoner → summarizer → verifier.
— Secure API credentials using Spring Config / Vault integration.
— Add async streaming responses (Server-Sent Events).
— Integrate observability: OpenTelemetry tracing, metrics with Micrometer + Prometheus.
— Add custom interceptors for logging prompts and responses.
— Provide JUnit tests for chain determinism and data isolation.
— Example endpoint: /ai/ask — retrieves context, runs reasoning chain, returns structured JSON answer.


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

— добавьте Guardrails для валидации ответов и отказов от токсичного контента;
— настройте дашборд Grafana: latency, token usage, accuracy, fallback-rate.

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41🔥1
✔️ Java-тест: Группировка строк по длине

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

📦 Задание

Реализуйте метод, который группирует строки по их длине:

public Map<Integer, List<String>> groupByLength(List<String> strings) {}


Требования

— Обработать null и пустой список → вернуть пустую Map
— Игнорировать null-элементы в списке
— Порядок строк в группах сохраняется
— Пустые строки группировать с ключом 0

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

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

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

#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍2🥱21
☕️ Java && Coffee

Делитесь фотографиями выходных.

До Балкан тоже начинает потихоньку добираться осень 🍁

Отправляйте фото в комментарии👇🏻

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

#DevLife
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
😍9🔥51👍1
👑 Магия IntelliJ IDEA: Refactor This

Когда рефакторите legacy-код или чистите архитектуру — приходится делать десятки преобразований. А вы знали, что в IDEA есть универсальное меню для всех рефакторингов?

🔹 Что делает

— Открывает контекстное меню со всеми доступными рефакторингами для текущего элемента
— Автоматически фильтрует опции в зависимости от контекста (переменная, метод, класс)
— Позволяет выбрать нужную операцию по цифре или первой букве

🔹 Зачем это нужно

— Не нужно помнить десятки отдельных хоткеев — один хоткей открывает все варианты
— Молниеносный доступ к редким, но мощным рефакторингам (Extract Parameter Object, Introduce Parameter, Pull Members Up)
— Безопасно переименовывает, перемещает и трансформирует код с учётом всех зависимостей
— Особенно полезно для сложных преобразований, которые вручную делать долго и опасно

🔹 Как использовать

— Поставьте курсор на переменную, метод, класс или любой элемент кода
— Нажмите Ctrl+Alt+Shift+T (Windows/Linux) или ⌃+T (macOS)
— Выберите нужный рефакторинг из списка: Extract Method, Rename, Change Signature, Inline и другие
— Настройте параметры → Enter → готово. IDEA сама обновит все использования

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍115🔥4
🔥 Как подключить MapStruct к проекту

MapStruct — это code-generation библиотека для автоматического маппинга между Java-объектами.

Работает на этапе компиляции, генерирует чистый код без рефлексии — в разы быстрее ModelMapper и Dozer.

1️⃣ Добавляем зависимости

Нужно добавить две зависимости: саму библиотеку mapstruct и процессор mapstruct-processor. Процессор отвечает за генерацию кода во время компиляции.

Критически важно правильно настроить maven-compiler-plugin, именно он запускает annotation processors. В секции annotationProcessorPaths прописываем mapstruct-processor, а если используете Lombok, добавляем его тоже плюс специальный binding для их совместной работы.

2️⃣ Создаём Mapper интерфейс

Создаёте обычный Java-интерфейс с аннотацией @Mapper(componentModel = "spring"). Параметр componentModel указывает, что MapStruct должен сгенерировать Spring-бин, который можно инжектить через конструктор.

В интерфейсе объявляете методы-конвертеры: например UserDto toDto(User user) или List<UserDto> toDtoList(List<User> users). MapStruct сам поймёт, какие поля во что маппить, если названия совпадают.

3️⃣ Настраиваем кастомный маппинг

Когда названия полей отличаются или нужна дополнительная логика, используйте аннотацию @Mapping:

▪️ source/target — для переименования полей: @Mapping(target = "fullName", source = "firstName")
▪️ expression — для Java-выражений: можно написать простую логику прямо в аннотации
▪️ ignore — чтобы игнорировать поле: @Mapping(target = "password", ignore = true)
▪️ dateFormat — для форматирования дат: автоматическая конвертация LocalDateTime в String
▪️ qualifiedByName — для вызова кастомных методов-конвертеров

4️⃣ Работа со вложенными объектами

MapStruct умеет автоматически маппить вложенные структуры. Если у вас в User есть поле Address, а в UserDto есть AddressDto — создайте отдельный AddressMapper и укажите его в параметре uses = {AddressMapper.class}.

MapStruct сам найдёт нужный маппер и применит его для вложенных объектов. Это работает рекурсивно для любой глубины вложенности.

5️⃣ Обновление существующих объектов

Часто нужно не создавать новый объект, а обновить существующий. Для этого используйте аннотацию @MappingTarget на втором параметре метода. MapStruct сгенерирует код, который обновит только нужные поля.

Добавьте @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) — тогда null-значения из DTO не затрут существующие данные в Entity.

6️⃣ Кастомные конвертеры

Для сложной логики добавьте в интерфейс default-методы. Пометьте их @Named("имя") и ссылайтесь через qualifiedByName. Например, для конвертации enum в русский текст или склейки нескольких полей.

Можно также создать отдельный класс с @Component и helper-методами, затем подключить его через параметр uses.

✔️ Что происходит под капотом

MapStruct анализирует ваш интерфейс на этапе компиляции, смотрит на типы полей, их названия и аннотации. Затем генерирует простой Java-код с прямым присвоением значений, никакой магии, рефлексии или proxy.

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

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍75🔥2🥱1
👀 Внутреннее устройство 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
🔥9👍3🎉1