Библиотека Java разработчика
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
Mostrar más📈 Análisis del canal de Telegram Библиотека Java разработчика
El canal Библиотека Java разработчика (@bookjava) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 10 121 suscriptores, ocupando la posición 11 760 en la categoría Tecnologías y Aplicaciones y el puesto 63 248 en la región Rusia.
📊 Métricas de audiencia y dinámica
Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 10 121 suscriptores.
Según los últimos datos del 25 agosto, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -74, y en las últimas 24 horas de -6, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 7.20%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 3.71% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 729 visualizaciones. En el primer día suele acumular 376 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 5.
- Intereses temáticos: El contenido se centra en temas clave como string, интерфейс, строка, boot, api.
📝 Descripción y política de contenido
El autor describe el recurso como un espacio para expresar opiniones subjetivas:
“📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.
По всем вопросам @evgenycarter
РКН clck.ru/3KoGeP”
Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 26 agosto, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.
equals() и hashCode()?
Эти методы нужны для корректного сравнения объектов и работы коллекций (`HashMap`, HashSet, HashTable и т. д.). Неправильная реализация может привести к неожиданным багам, которые трудно отловить.
✅ Основные правила для equals()
1️⃣ Рефлексивность – x.equals(x) должно всегда возвращать true.
2️⃣ Симметричность – x.equals(y) должно возвращать тот же результат, что и y.equals(x).
3️⃣ Транзитивность – если x.equals(y) и y.equals(z), то x.equals(z).
4️⃣ Стабильность – если объекты не менялись, результат вызова equals() не должен меняться.
5️⃣ Не null – x.equals(null) всегда должен возвращать false.
Пример корректного equals():
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
MyClass myClass = (MyClass) obj;
return Objects.equals(field1, myClass.field1) &&
Objects.equals(field2, myClass.field2);
}
✅ Основные правила для hashCode()
🔹 Если equals() возвращает true, то hashCode() должен быть одинаковым.
🔹 Если equals() возвращает false, то hashCode() может быть одинаковым, но лучше минимизировать коллизии.
🔹 hashCode() должен быть быстрым и эффективно распределять объекты.
Пример hashCode():
@Override
public int hashCode() {
return Objects.hash(field1, field2);
}
❌ Частые ошибки
🚫 Использование == вместо equals() для объектов.
🚫 Неопределенный hashCode(), из-за чего HashMap работает некорректно.
🚫 Изменяемые поля в hashCode() – при изменении объекта он может потеряться в HashMap.
Используйте Objects.equals() и Objects.hash(), чтобы избежать рутины!
📢 Как вы реализуете equals() и hashCode()? Были ли у вас баги из-за их неправильной работы? Делитесь в комментариях!
📲 Мы в MAX
👉@BookJavax, temp, data — дай переменным осмысленные названия. Например, вместо:
int d = 365;
лучше:
int daysInYear = 365;
✅ Меньше вложенности
Глубокая вложенность усложняет чтение. Вместо этого можно использовать guard clauses:
❌ Плохо:
if (user != null) {
if (user.isActive()) {
process(user);
}
}
✅ Хорошо:
if (user == null) return;
if (!user.isActive()) return;
process(user);
✅ Разбивай код на небольшие методы
Методы должны делать только одну вещь и быть короткими (10-20 строк). Если метод раздувается, попробуй выделить логику в отдельные функции.
✅ Избавляйся от магических чисел
Если в коде встречаются числа типа 3.14159, 86400 — вынеси их в константы:
private static final int SECONDS_IN_A_DAY = 86400;
✅ Используй Optional и Collections.emptyList()
Чтобы избежать NullPointerException, возвращай Optional<T> вместо null и Collections.emptyList() вместо пустых списков.
✅ Форматируй код по стандарту
Используй Code Style в IDE или автоформатирование (Ctrl + Alt + L в IntelliJ IDEA).
Какой из этих приемов ты уже используешь? Может, у тебя есть свои лайфхаки? Делись в комментариях! 🚀
📲 Мы в MAX
👉@BookJava
Query query = entityManager.createQuery("SELECT u FROM User u WHERE u.age > :age")
.setParameter("age", 25)
.setHint("org.hibernate.cacheable", true);
List<User> users = query.getResultList();
Здесь "org.hibernate.cacheable" позволяет кешировать результат запроса.
2. Аннотация @QueryHint (Spring Data JPA)
@QueryHints({@QueryHint(name = "org.hibernate.readOnly", value = "true")})
@Query("SELECT u FROM User u WHERE u.status = 'ACTIVE'")
List<User> findActiveUsers();
Этот hint указывает Hibernate, что данные только для чтения, что может ускорить выполнение.
🔍 Полезные Query Hints
Вот несколько полезных хинтов для Hibernate:
- org.hibernate.cacheable = true – разрешает кеширование результата.
- org.hibernate.fetchSize = N – задаёт количество строк, загружаемых за раз.
- org.hibernate.readOnly = true – отключает слежение за изменениями (ускоряет SELECT).
- org.hibernate.comment = 'My custom hint' – добавляет комментарий к запросу.
❗ Когда использовать?
✅ При сложных JOIN-запросах
✅ При работе с кешем
✅ Для больших выборок (fetchSize)
✅ Если запрос не изменяет данные (readOnly)
А вы используете Query Hints в своих проектах? Делись в комментариях! 👇
📲 Мы в MAX
👉@BookJava
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
Теперь представьте, что у нас есть список пользователей, и мы хотим:
✅ Оставить только совершеннолетних
✅ Отсортировать их по возрасту
✅ Преобразовать в список имен
Легко справимся с этим с помощью Stream API:
List<User> users = List.of(
new User("Алекс", 25),
new User("Мария", 17),
new User("Иван", 30),
new User("Ольга", 20)
);
List<String> adultNames = users.stream()
.filter(user -> user.age >= 18) // Фильтрация
.sorted(Comparator.comparingInt(user -> user.age)) // Сортировка
.map(user -> user.name) // Преобразование
.toList();
System.out.println(adultNames); // [Ольга, Алекс, Иван]
Что здесь происходит?
🔹 filter(user -> user.age >= 18): Убираем несовершеннолетних.
🔹 sorted(Comparator.comparingInt(user -> user.age)): Сортируем по возрасту.
🔹 map(user -> user.name): Преобразуем User в String, оставляя только имена.
🔹 toList(): Собираем результат в список.
Stream API позволяет писать чистый, читаемый и декларативный код, избавляя от лишних циклов.
А вы активно используете Stream API в своих проектах? Делитесь в комментариях! 🚀
📲 Мы в MAX
👉@BookJava@Async и не напороться на проблемы?
Сегодня я расскажу вам, как правильно использовать аннотацию @Async в Spring Boot, чтобы асинхронные задачи работали стабильно и без неожиданностей.
🔹 Что делает @Async?
Эта аннотация позволяет выполнять методы в отдельном потоке, не блокируя основной поток приложения. Это удобно, когда нужно, например, отправить email или выполнить сложный расчет без задержки ответа пользователю.
🔹 Как правильно использовать?
1️⃣ Включите поддержку асинхронности
Добавьте в главный класс Spring Boot:
@EnableAsync
@SpringBootApplication
public class MyApplication {
2️⃣ Аннотируйте метод в сервисе
@Service
public class EmailService {
@Async
public void sendEmail(String email) {
System.out.println("Отправка email: " + email + " в потоке " + Thread.currentThread().getName());
}
}
3️⃣ Вызывайте метод асинхронно
@Component
public class NotificationSender {
private final EmailService emailService;
public NotificationSender(EmailService emailService) {
this.emailService = emailService;
}
public void notifyUser(String email) {
emailService.sendEmail(email);
System.out.println("Метод notifyUser выполняется в потоке " + Thread.currentThread().getName());
}
}
🔹 Частые ошибки и их решения
❌ Вызываете асинхронный метод внутри того же класса?
Spring не будет проксировать вызов, и @Async просто не сработает. Выносите метод в отдельный бин!
❌ Нет пула потоков?
По умолчанию Spring использует SimpleAsyncTaskExecutor, который создает новый поток для каждой задачи. Это может перегрузить систему. Лучше явно указать пул:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.initialize();
return executor;
}
}
📲 Мы в MAX
👉@BookJavaint a = 5;
✅ int maxRetries = 5;
2️⃣ Короткие методы
Огромные методы с кучей логики сложно читать и поддерживать. Разбивайте их на мелкие, понятные части. Хороший метод делает только одну вещь и делает её хорошо.
3️⃣ Минимум вложенности
Чем больше if-else и циклов внутри друг друга — тем сложнее понимать код. Используйте ранний выход (return, continue, break), чтобы уменьшить вложенность.
4️⃣ Избегайте магических чисел
Никогда не вставляйте числа или строки прямо в код. Заводите константы.
❌ if (status == 3) {...}
✅ if (status == ORDER_COMPLETED) {...}
5️⃣ Отказ от комментариев в пользу читаемого кода
Комментарий не должен объяснять что делает код — это обязанность самого кода! Если без комментариев неясно, что происходит, значит, нужно переписать код.
👉 Какое из этих правил вы чаще всего нарушаете? Или, может, у вас есть своё золотое правило чистого кода? Пишите в комментариях!
📲 Мы в MAX
👉@BookJavaOptional в Java – не просто контейнер!
Сегодня разберём важную тему: Optional в Java – это не просто удобный способ избежать null, но и мощный инструмент для работы с потоками данных.
🚀 Как правильно использовать Optional?
1️⃣ Избегаем null-чеков
Раньше код был полон if (obj != null), но теперь:
Optional<String> name = Optional.ofNullable(user.getName());
name.ifPresent(System.out::println);
Это делает код чище и понятнее.
2️⃣ Комбинируем с map() и flatMap()
Если у нас есть объект, внутри которого другой объект, а внутри него – ещё один, Optional поможет избежать вложенных if-else:
Optional<String> city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity);
Всё элегантно и лаконично!
3️⃣ Используем orElse() и orElseGet() правильно
❌ Антипаттерн – всегда выполняет new Object():
User user = optionalUser.orElse(new User());
✅ Лучше так – orElseGet() вызовется только если optionalUser пуст:
User user = optionalUser.orElseGet(User::new);
Это особенно важно, если объект тяжёлый в создании.
4️⃣ orElseThrow() – избавляемся от null вообще
Когда отсутствие значения – это ошибка, не бойтесь выбрасывать исключение:
User user = optionalUser.orElseThrow(() -> new RuntimeException("User not found"));
Это делает код безопаснее!
❌ Когда не стоит использовать Optional?
- В полях сущностей (например, JPA) – это может ухудшить производительность.
- Для коллекций – лучше возвращать пустую коллекцию, а не Optional<List<T>>.
📲 Мы в MAX
👉@BookJava@Slf4j
Сегодня я расскажу вам о @Slf4j из библиотеки Lombok и о том, как его правильно использовать, чтобы ваш код стал чище и удобнее.
Что такое @Slf4j?
Это аннотация, которая добавляет в ваш класс статическое поле логгера org.slf4j.Logger. Вместо того чтобы писать:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);
}
Достаточно одной аннотации:
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MyService {
}
Теперь в коде можно просто писать:
log.info("Приложение запущено");
log.error("Произошла ошибка: {}", exception.getMessage());
На что обратить внимание?
1️⃣ @Slf4j использует SLF4J API, поэтому вам все равно потребуется подключить реализацию логирования, например Logback:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
2️⃣ Если нужен другой логгер, можно использовать альтернативные аннотации:
- @Log — для java.util.logging
- @Log4j — для Apache Log4j
- @Log4j2 — для Log4j2
3️⃣ Не забывайте про уровень логирования!
Используйте debug для отладки, info для полезных сообщений, warn для предупреждений и error для ошибок.
Вывод: @Slf4j — это удобный инструмент, который избавляет от лишнего кода и упрощает работу с логированием. Если вы еще не используете Lombok для логирования, самое время попробовать!
📲 Мы в MAX
👉@BookJava
Optional<String> optional = Optional.ofNullable(getValue());
if (optional.isPresent()) {
System.out.println(optional.get());
}
Почему плохо?
- get() без проверки – потенциальная ловушка.
- Лишний if – можно сделать проще.
✅ Как надо:
Optional.ofNullable(getValue()).ifPresent(System.out::println);
или, если нужно значение по умолчанию:
String value = Optional.ofNullable(getValue()).orElse("Default Value");
Крутые приемы с Optional:
✔ orElseGet – лениво вычисляет значение
✔ orElseThrow – выбрасывает исключение, если Optional пуст
✔ map и flatMap – позволяют трансформировать данные
🔥 Советы:
1️⃣ Не используйте Optional для полей классов – это не сериализуемо.
2️⃣ Не передавайте Optional в аргументах методов – это антипаттерн.
3️⃣ Optional хорош для возвращаемых значений – используйте его вместо null.
Как вы используете Optional в своих проектах? Делитесь в комментариях!
📲 Мы в MAX
👉@BookJavaCompletableFuture в Java
Сегодня я хочу рассказать вам про CompletableFuture — мощный инструмент для работы с асинхронными операциями в Java. Если вам приходилось ждать выполнения долгих задач в коде и хотелось бы улучшить производительность, то этот пост для вас! 🚀
🔹 Что такое CompletableFuture?
CompletableFuture — это часть java.util.concurrent с Java 8, которая позволяет выполнять асинхронные задачи и удобно комбинировать их. В отличие от обычного Future, CompletableFuture поддерживает цепочки вызовов, композицию задач и обработку ошибок.
🔹 Пример использования
Допустим, у нас есть сервис, который загружает данные по сети. Обычный подход синхронного вызова будет блокировать поток, но с CompletableFuture мы можем избежать этого:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
System.out.println("Загружаем данные...");
sleep(2000);
return "Данные загружены";
}).thenApply(data -> {
System.out.println("Обрабатываем: " + data);
return data.toUpperCase();
}).thenAccept(System.out::println)
.exceptionally(ex -> {
System.out.println("Ошибка: " + ex.getMessage());
return null;
});
sleep(3000); // Даем время асинхронной операции завершиться
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
🔹 Разбор кода
1️⃣ supplyAsync() — запускает асинхронную задачу в отдельном потоке.
2️⃣ thenApply() — позволяет обработать результат (например, изменить его формат).
3️⃣ thenAccept() — принимает готовый результат и выполняет действие.
4️⃣ exceptionally() — обрабатывает возможные ошибки.
🔹 Зачем это нужно?
✅ Улучшает производительность за счет асинхронного выполнения.
✅ Избегает блокировки основного потока.
✅ Позволяет легко комбинировать операции.
Используете ли вы CompletableFuture в своих проектах? Делитесь опытом в комментариях! 👇
📲 Мы в MAX
👉@BookJava-Xms<size> – задает начальный размер кучи.
- -Xmx<size> – задает максимальный размер кучи.
- -XX:NewRatio=<n> – определяет соотношение между молодым и старым поколением.
- -XX:SurvivorRatio=<n> – соотношение между Eden и Survivor.
⚡ Гаражная сборка (GC):
- -XX:+UseG1GC – включает G1 Garbage Collector (по умолчанию в Java 9+).
- -XX:+UseParallelGC – включает Parallel GC.
- -XX:+UseZGC – включает экспериментальный ZGC (минимальная пауза).
- -XX:+UseShenandoahGC – еще один GC с низкими задержками.
🛠️ Диагностика и отладка:
- -XX:+PrintGCDetails – подробный вывод информации о сборке мусора.
- -XX:+HeapDumpOnOutOfMemoryError – дамп памяти при OOM.
- -XX:HeapDumpPath=<path> – указывает путь для дампа памяти.
- -XX:+ExitOnOutOfMemoryError – завершает JVM при OOM.
🏎️ Оптимизация JIT:
- -XX:+TieredCompilation – адаптивная компиляция кода.
- -XX:+UseStringDeduplication – уменьшает использование памяти строками.
- -XX:+AlwaysPreTouch – аллокация памяти заранее (полезно для больших heap'ов).
Попробуйте поэкспериментировать с этими параметрами и посмотрите, как они влияют на производительность вашего приложения!
Какие параметры JVM используете вы? Пишите в комментариях!
📲 Мы в MAX
👉@BookJava@Transactional в Spring: Где Подводные Камни?
Давайте обсудимм одну из самых популярных аннотаций в Spring — @Transactional. Многие знают, что она используется для управления транзакциями, но не все понимают, как она работает под капотом и какие проблемы могут возникнуть. Давайте разбираться!
🔍 Как работает @Transactional?
Когда вы помечаете метод @Transactional, Spring проксирует этот метод и оборачивает его в транзакцию. Это значит, что до начала метода открывается транзакция, а после — либо коммитится (если нет ошибок), либо откатывается (если есть исключение).
Но тут важно помнить:
🔹 @Transactional работает только на public методах (если используется Spring AOP).
🔹 Вызовы методов внутри одного класса не учитывают @Transactional. Если вызвать метод, аннотированный @Transactional, внутри другого метода того же класса, транзакция не создастся.
🔹 По умолчанию, транзакция откатывается только при RuntimeException. Если бросить checked-исключение, Spring не откатит транзакцию.
⚠️ Распространённые ошибки
❌ Аннотация на private методе
Транзакция просто не будет работать, так как Spring AOP не перехватит вызов.
❌ Вызов @Transactional метода внутри того же класса
Транзакция не создастся, так как вызов происходит без участия Spring Proxy. Решение — выносить такие методы в отдельный бин или использовать TransactionTemplate.
❌ Неправильный rollback
Если в методе выбрасывается checked-исключение, Spring по умолчанию **не откатывает** транзакцию. Чтобы изменить это поведение, нужно явно указать `@Transactional(rollbackFor = Exception.class).
✅ Как избежать проблем?
✔️ Всегда ставьте @Transactional на публичные методы.
✔️ Вызывайте @Transactional-методы только через Spring-управляемые бины.
✔️ Контролируйте rollback через rollbackFor.
✔️ Используйте propagation = REQUIRES_NEW, если хотите создать новую независимую транзакцию.
Кто сталкивался с неожиданным поведением @Transactional? Давайте обсудим в комментариях!
📲 Мы в MAX
👉@BookJava
String str = new String("Hello"); // Избыточно
✅ Хорошо:
String str = "Hello"; // Используем строковый пул
То же самое касается Integer.valueOf() вместо new Integer().
🔄 2. Используйте StringBuilder вместо конкатенации в цикле
Если вы объединяете строки в цикле, StringBuilder будет значительно быстрее.
❌ Плохо:
String result = "";
for (int i = 0; i < 100; i++) {
result += i; // Создает новый объект String на каждой итерации
}
✅ Хорошо:
StringBuilder result = new StringBuilder();
for (int i = 0; i < 100; i++) {
result.append(i);
}
Такой код работает в разы быстрее!
🏎 3. Правильно выбирайте коллекции
Используйте ArrayList, если не нужна частая вставка/удаление элементов в середине списка.
Используйте HashSet, если важны уникальные значения и не нужен порядок.
Используйте LinkedList, если нужна частая вставка/удаление в середине списка.
⚡ 4. Не злоупотребляйте Stream API
Да, Stream API удобен, но иногда он замедляет код. Например:
❌ Плохо:
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, Integer::sum);
✅ Хорошо:
int sum = 0;
for (int num : numbers) {
sum += num;
}
Цикл быстрее, потому что не тратит время на создание объектов и лямбды.
🔥 Вывод
Оптимизация — это не просто ускорение кода, но и улучшение его читаемости и поддержки. Используйте правильные структуры данных, избегайте лишних аллокаций, выбирайте оптимальные конструкции.
А какие советы по оптимизации Java кода используете вы? Пишите в комментариях! 👇 🚀
📲 Мы в MAX
👉@BookJavacollection.stream()
🔹 Из массива: Arrays.stream(array)
🔹 Из набора элементов: Stream.of(1, 2, 3)
🔹 Бесконечный поток: Stream.iterate(0, n -> n + 1)
🔹 Бесконечный поток с ограничением (Java 9): Stream.iterate(1, n -> n < 100, n -> n * 2)
🔹 Генерация элементов: Stream.generate(Math::random)
🔹 Диапазон значений:
- IntStream.range(1, 5) (1, 2, 3, 4)
- IntStream.rangeClosed(1, 5) (1, 2, 3, 4, 5)
🔹 Из файла: Files.lines(Path.of("file.txt"))
🔹 Из строки: "abc".chars()
Промежуточные операции
Stream API поддерживает множество преобразований. Наиболее распространенные:
✔️ Фильтрация: filter(Predicate<T>) – оставляет только элементы, соответствующие условию.
✔️ Удаление дубликатов: distinct() – исключает повторяющиеся элементы.
✔️ Ограничение количества: limit(n) – берет первые n элементов.
✔️ Сортировка: sorted() – упорядочивает элементы.
Менее очевидные операции
🔹 map(Function<T, R>) – применяет функцию к каждому элементу.
🔹 flatMap(Function<T, Stream<R>>) – «разворачивает» элементы из вложенных структур.
🔹 takeWhile(Predicate<T>) (Java 9) – берет элементы, пока выполняется условие.
🔹 dropWhile(Predicate<T>) (Java 9) – пропускает элементы, пока условие выполняется.
🔹 peek(Consumer<T>) – выполняет действие без изменения элементов (удобно для логирования).
Конечные операции
Стрим начинает обработку данных только при вызове конечной операции:
📌 Коллекционирование:
- collect(Collectors.toList()) – собирает в List.
- collect(Collectors.toSet()) – собирает в Set.
📌 Поиск элементов:
- findFirst() – первый элемент.
- findAny() – любой элемент (оптимизирован для параллельных потоков).
- anyMatch(Predicate<T>) – хотя бы один элемент удовлетворяет условию.
- allMatch(Predicate<T>) – все элементы удовлетворяют условию.
- noneMatch(Predicate<T>) – ни один элемент не удовлетворяет условию.
📌 Агрегация:
- min(Comparator<T>) – минимальный элемент.
- max(Comparator<T>) – максимальный элемент.
- count() – количество элементов.
- reduce(BinaryOperator<T>) – свертка элементов в одно значение.
📌 Побочные эффекты:
- forEach(Consumer<T>) – выполняет действие над каждым элементом.
- forEachOrdered(Consumer<T>) – выполняет действие, сохраняя порядок (важно для параллельных потоков).
Особенности работы со Stream API
1️⃣ Стрим – это не структура данных
Он лишь обходит источник, выполняя операции лениво.
2️⃣ Стрим нельзя использовать повторно
После вызова конечной операции повторное использование потока приведет к IllegalStateException.
3️⃣ Исходные данные не изменяются
Методы Stream API не модифицируют исходную коллекцию.
Разбор сложных случаев
❌ Ошибочный код:
Stream.of(-1, 0, 1).max(Math::max).get();
✅ Почему ошибка?
Метод max() принимает Comparator<T>, но Math.max(a, b) – это BiFunction<Integer, Integer, Integer>. Они не эквивалентны!
ℹ️ Решение:
Stream.of(-1, 0, 1).max(Integer::compareTo).get(); // Вернет 1
📲 Мы в MAX
👉@BookJavaCompletableFuture в Java: Асинхронность без боли
Всем добрый вечер! Сегодня расскажу про CompletableFuture — мощный инструмент для работы с асинхронными операциями в Java. Если вы хотите избавиться от блокирующего кода и сложных коллбэков, этот пост для вас!
🤔 Что такое CompletableFuture?
Это часть java.util.concurrent, позволяющая писать асинхронный код в декларативном стиле, без создания сложных цепочек Thread и ExecutorService.
🚀 Базовый пример использования
Допустим, у нас есть задача загрузить данные с сервера. Как это сделать асинхронно?
import java.util.concurrent.CompletableFuture;
public class AsyncExample {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
// Имитация долгого запроса
sleep(2000);
return "Данные загружены";
}).thenAccept(result ->
System.out.println("Результат: " + result)
);
System.out.println("Задача запущена, ждем результат...");
sleep(3000); // Чтобы main не завершился раньше времени
}
private static void sleep(int millis) {
try { Thread.sleep(millis); }
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
}
🔍 Разбираем код
1️⃣ supplyAsync() — выполняет операцию в фоновом потоке.
2️⃣ thenAccept() — получает результат и выполняет код после завершения.
3️⃣ Главный поток продолжает работать, не блокируя выполнение.
🛠 Расширяем функционал
Можно комбинировать задачи:
CompletableFuture.supplyAsync(() -> "Привет, ")
.thenApply(greeting -> greeting + "мир!")
.thenAccept(System.out::println);
✅ thenApply() изменяет данные перед следующим шагом.
✅ thenAccept() выполняет финальную операцию.
📌 Где использовать?
🔹 Запросы к API без блокировки
🔹 Асинхронная обработка данных
🔹 Параллельные вычисления
📲 Мы в MAX
👉@BookJava