🧠 Как избежать N+1 при использовании
Одна из самых коварных ловушек JPA - это N+1 проблема. Особенно часто она проявляется при
Вы загружаете список авторов, а потом проходите по каждому и вызываете
📌 Решение -
Оба варианта решают проблему, но
⚡️ Или через JPQL:
💡 Совет: всегда думайте о графе объектов. Если вам нужно сразу подтянуть связанные сущности — делайте это явно. Не надейтесь на LAZY по умолчанию.
⚠️ Осторожно с пагинацией и JOIN FETCH — могут появиться дубликаты или проблемы с
👉@BookJava
@OneToMany
в JPA Одна из самых коварных ловушек JPA - это N+1 проблема. Особенно часто она проявляется при
@OneToMany
, например:
@Entity
class Author {
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Book> books;
}
Вы загружаете список авторов, а потом проходите по каждому и вызываете
getBooks()
— и BAM 💥: 1 запрос на авторов и N запросов на книги. 📌 Решение -
@EntityGraph
или JOIN FETCH
Оба варианта решают проблему, но
@EntityGraph
— декларативный и более гибкий способ:
@EntityGraph(attributePaths = "books")
List<Author> findAll(); // Spring Data JPA
⚡️ Или через JPQL:
@Query("SELECT a FROM Author a JOIN FETCH a.books")
List<Author> findAllWithBooks();
💡 Совет: всегда думайте о графе объектов. Если вам нужно сразу подтянуть связанные сущности — делайте это явно. Не надейтесь на LAZY по умолчанию.
⚠️ Осторожно с пагинацией и JOIN FETCH — могут появиться дубликаты или проблемы с
LIMIT
. В таких случаях лучше использовать @BatchSize
или подзапросы.👉@BookJava
👍5
🧠 JPA: подводный камень с
Сегодня покажу, почему
📌 Суть проблемы
При использовании
Запрос
1 запрос на
💡 Почему это больно
Даже если вы используете
⚠️ Особенно опасно при pagination
Если вы делаете
✅ Что делать
1. Делайте
2. Если нужно подгрузить коллекцию — используйте
Или вручную:
👉 Следи за
👉@BookJava
@ElementCollection
и fetch = FetchType.EAGER
Сегодня покажу, почему
@ElementCollection(fetch = FetchType.EAGER)
— скрытая угроза производительности и неожиданного поведения.📌 Суть проблемы
При использовании
@ElementCollection
с EAGER
Hibernate делает отдельный SELECT на каждую коллекцию, даже при JOIN FETCH
на родительскую сущность.
@Entity
class User {
@Id Long id;
@ElementCollection(fetch = FetchType.EAGER)
List<String> tags;
}
Запрос
findAll()
приведёт к N+1 проблеме: 1 запрос на
User
, потом по одному на каждый tags
.💡 Почему это больно
Даже если вы используете
JOIN FETCH
на User
, Hibernate не может сделать JOIN
на @ElementCollection
. Это ограничение — Hibernate всегда грузит коллекцию отдельным запросом.⚠️ Особенно опасно при pagination
Если вы делаете
Page<User>
— Hibernate сначала грузит User
'ов, а затем делает N запросов на коллекции. В проде это быстро становится проблемой.✅ Что делать
1. Делайте
fetch = FetchType.LAZY
(по умолчанию так и есть).2. Если нужно подгрузить коллекцию — используйте
@BatchSize
:
@ElementCollection
@BatchSize(size = 20)
List<String> tags;
Или вручную:
SELECT u FROM User u LEFT JOIN FETCH u.tags WHERE u.id IN :ids
👉 Следи за
@ElementCollection
— он не так прост, как кажется.👉@BookJava
👍6
На открытом уроке «Облако в кармане: запускаем всю инфраструктуру для теста при сборке» от OTUS мы расскажем, как избежать ручных настроек и запусков. Вместо этого вы научитесь автоматизировать весь процесс с помощью Docker, DockerCompose и TestContainers, интегрируя их с Gradle.
Урок полезен для разработчиков на Kotlin и Java, которые работают с автотестами — интеграционными и end-to-end.
В ходе урока вы освоите:
▫️Автоматический запуск всей необходимой инфраструктуры для тестирования.
▫️Создание Docker-образов для тестов и деплоя.
▫️Разработку автотестов, которые поднимут ваши навыки на новый уровень.
Участники получат скидку на курс «Kotlin Backend Developer. Professional».
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Сегодня покажу вам 🧠 один тонкий момент в работе с
Многие пишут так:
⚠️ Это антипаттерн! Вы теряете суть
📌 Правильный способ — использовать функциональный стиль:
Или ещё элегантнее:
💡 Подходы:
-
-
-
-
📈 Выгоды:
- Код становится компактнее
- Безопаснее при рефакторинге
- Лучше для чтения в потоковых операциях (
🛠 Если нужно вынуть значение — используйте
👉 Отказывайтесь от
👉@BookJava
Optional
, который часто ловит даже опытных.Многие пишут так:
Optional<String> optional = getValue();
if (optional.isPresent()) {
doSomething(optional.get());
}
⚠️ Это антипаттерн! Вы теряете суть
Optional
и рискуете ошибками в многопоточке.📌 Правильный способ — использовать функциональный стиль:
getValue().ifPresent(this::doSomething);
Или ещё элегантнее:
getValue()
.map(this::transform)
.filter(this::isValid)
.ifPresent(this::doSomething);
💡 Подходы:
-
map
для преобразования значения-
filter
для отсеивания ненужных-
orElse
, orElseGet
, orElseThrow
для обработки отсутствия-
ifPresentOrElse
в Java 9+ для двух вариантов действий📈 Выгоды:
- Код становится компактнее
- Безопаснее при рефакторинге
- Лучше для чтения в потоковых операциях (
Stream API
)🛠 Если нужно вынуть значение — используйте
orElseThrow()
вместо .get()
:
String value = getValue().orElseThrow(() -> new IllegalStateException("Value not found"));
👉 Отказывайтесь от
.isPresent()
и .get()
связки — используйте силу функционального подхода! 🚀👉@BookJava
👍7👎5
🚀 Подборка Telegram каналов для программистов
Системное администрирование, DevOps 📌
https://t.me/bash_srv Bash Советы
https://t.me/win_sysadmin Системный Администратор Windows
https://t.me/sysadmin_girl Девочка Сисадмин
https://t.me/srv_admin_linux Админские угодья
https://t.me/linux_srv Типичный Сисадмин
https://t.me/devopslib Библиотека девопса | DevOps, SRE, Sysadmin
https://t.me/linux_odmin Linux: Системный администратор
https://t.me/devops_star DevOps Star (Звезда Девопса)
https://t.me/i_linux Системный администратор
https://t.me/linuxchmod Linux
https://t.me/sys_adminos Системный Администратор
https://t.me/tipsysdmin Типичный Сисадмин (фото железа, было/стало)
https://t.me/sysadminof Книги для админов, полезные материалы
https://t.me/i_odmin Все для системного администратора
https://t.me/i_odmin_book Библиотека Системного Администратора
https://t.me/i_odmin_chat Чат системных администраторов
https://t.me/i_DevOps DevOps: Пишем о Docker, Kubernetes и др.
https://t.me/sysadminoff Новости Линукс Linux
1C разработка 📌
https://t.me/odin1C_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://t.me/DevLab1C 1С:Предприятие 8
https://t.me/razrab_1C 1C Разработчик
https://t.me/buh1C_prog 1C Программист | Бухгалтерия и Учёт
https://t.me/rabota1C_rus Вакансии для программистов 1С
Программирование C++📌
https://t.me/cpp_lib Библиотека C/C++ разработчика
https://t.me/cpp_knigi Книги для программистов C/C++
https://t.me/cpp_geek Учим C/C++ на примерах
Программирование Python 📌
https://t.me/pythonofff Python академия.
https://t.me/BookPython Библиотека Python разработчика
https://t.me/python_real Python подборки на русском и английском
https://t.me/python_360 Книги по Python
Java разработка 📌
https://t.me/BookJava Библиотека Java разработчика
https://t.me/java_360 Книги по Java Rus
https://t.me/java_geek Учим Java на примерах
GitHub Сообщество 📌
https://t.me/Githublib Интересное из GitHub
Базы данных (Data Base) 📌
https://t.me/database_info Все про базы данных
Мобильная разработка: iOS, Android 📌
https://t.me/developer_mobila Мобильная разработка
https://t.me/kotlin_lib Подборки полезного материала по Kotlin
Фронтенд разработка 📌
https://t.me/frontend_1 Подборки для frontend разработчиков
https://t.me/frontend_sovet Frontend советы, примеры и практика!
https://t.me/React_lib Подборки по React js и все что с ним связано
Разработка игр 📌
https://t.me/game_devv Все о разработке игр
Библиотеки 📌
https://t.me/book_for_dev Книги для программистов Rus
https://t.me/programmist_of Книги по программированию
https://t.me/proglb Библиотека программиста
https://t.me/bfbook Книги для программистов
БигДата, машинное обучение 📌
https://t.me/bigdata_1 Big Data, Machine Learning
Программирование 📌
https://t.me/bookflow Лекции, видеоуроки, доклады с IT конференций
https://t.me/rust_lib Полезный контент по программированию на Rust
https://t.me/golang_lib Библиотека Go (Golang) разработчика
https://t.me/itmozg Программисты, дизайнеры, новости из мира IT
https://t.me/php_lib Библиотека PHP программиста 👨🏼💻👩💻
https://t.me/nodejs_lib Подборки по Node js и все что с ним связано
https://t.me/ruby_lib Библиотека Ruby программиста
https://t.me/lifeproger Жизнь программиста. Авторский канал.
QA, тестирование 📌
https://t.me/testlab_qa Библиотека тестировщика
Шутки программистов 📌
https://t.me/itumor Шутки программистов
Защита, взлом, безопасность 📌
https://t.me/thehaking Канал о кибербезопасности
https://t.me/xakep_2 Хакер Free
Книги, статьи для дизайнеров 📌
https://t.me/ux_web Статьи, книги для дизайнеров
Математика 📌
https://t.me/Pomatematike Канал по математике
https://t.me/phis_mat Обучающие видео, книги по Физике и Математике
https://t.me/matgeoru Математика | Геометрия | Логика
Excel лайфхак📌
https://t.me/Excel_lifehack
https://t.me/mir_teh Мир технологий (Technology World)
Вакансии 📌
https://t.me/sysadmin_rabota Системный Администратор
https://t.me/progjob Вакансии в IT
Системное администрирование, DevOps 📌
https://t.me/bash_srv Bash Советы
https://t.me/win_sysadmin Системный Администратор Windows
https://t.me/sysadmin_girl Девочка Сисадмин
https://t.me/srv_admin_linux Админские угодья
https://t.me/linux_srv Типичный Сисадмин
https://t.me/devopslib Библиотека девопса | DevOps, SRE, Sysadmin
https://t.me/linux_odmin Linux: Системный администратор
https://t.me/devops_star DevOps Star (Звезда Девопса)
https://t.me/i_linux Системный администратор
https://t.me/linuxchmod Linux
https://t.me/sys_adminos Системный Администратор
https://t.me/tipsysdmin Типичный Сисадмин (фото железа, было/стало)
https://t.me/sysadminof Книги для админов, полезные материалы
https://t.me/i_odmin Все для системного администратора
https://t.me/i_odmin_book Библиотека Системного Администратора
https://t.me/i_odmin_chat Чат системных администраторов
https://t.me/i_DevOps DevOps: Пишем о Docker, Kubernetes и др.
https://t.me/sysadminoff Новости Линукс Linux
1C разработка 📌
https://t.me/odin1C_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://t.me/DevLab1C 1С:Предприятие 8
https://t.me/razrab_1C 1C Разработчик
https://t.me/buh1C_prog 1C Программист | Бухгалтерия и Учёт
https://t.me/rabota1C_rus Вакансии для программистов 1С
Программирование C++📌
https://t.me/cpp_lib Библиотека C/C++ разработчика
https://t.me/cpp_knigi Книги для программистов C/C++
https://t.me/cpp_geek Учим C/C++ на примерах
Программирование Python 📌
https://t.me/pythonofff Python академия.
https://t.me/BookPython Библиотека Python разработчика
https://t.me/python_real Python подборки на русском и английском
https://t.me/python_360 Книги по Python
Java разработка 📌
https://t.me/BookJava Библиотека Java разработчика
https://t.me/java_360 Книги по Java Rus
https://t.me/java_geek Учим Java на примерах
GitHub Сообщество 📌
https://t.me/Githublib Интересное из GitHub
Базы данных (Data Base) 📌
https://t.me/database_info Все про базы данных
Мобильная разработка: iOS, Android 📌
https://t.me/developer_mobila Мобильная разработка
https://t.me/kotlin_lib Подборки полезного материала по Kotlin
Фронтенд разработка 📌
https://t.me/frontend_1 Подборки для frontend разработчиков
https://t.me/frontend_sovet Frontend советы, примеры и практика!
https://t.me/React_lib Подборки по React js и все что с ним связано
Разработка игр 📌
https://t.me/game_devv Все о разработке игр
Библиотеки 📌
https://t.me/book_for_dev Книги для программистов Rus
https://t.me/programmist_of Книги по программированию
https://t.me/proglb Библиотека программиста
https://t.me/bfbook Книги для программистов
БигДата, машинное обучение 📌
https://t.me/bigdata_1 Big Data, Machine Learning
Программирование 📌
https://t.me/bookflow Лекции, видеоуроки, доклады с IT конференций
https://t.me/rust_lib Полезный контент по программированию на Rust
https://t.me/golang_lib Библиотека Go (Golang) разработчика
https://t.me/itmozg Программисты, дизайнеры, новости из мира IT
https://t.me/php_lib Библиотека PHP программиста 👨🏼💻👩💻
https://t.me/nodejs_lib Подборки по Node js и все что с ним связано
https://t.me/ruby_lib Библиотека Ruby программиста
https://t.me/lifeproger Жизнь программиста. Авторский канал.
QA, тестирование 📌
https://t.me/testlab_qa Библиотека тестировщика
Шутки программистов 📌
https://t.me/itumor Шутки программистов
Защита, взлом, безопасность 📌
https://t.me/thehaking Канал о кибербезопасности
https://t.me/xakep_2 Хакер Free
Книги, статьи для дизайнеров 📌
https://t.me/ux_web Статьи, книги для дизайнеров
Математика 📌
https://t.me/Pomatematike Канал по математике
https://t.me/phis_mat Обучающие видео, книги по Физике и Математике
https://t.me/matgeoru Математика | Геометрия | Логика
Excel лайфхак📌
https://t.me/Excel_lifehack
https://t.me/mir_teh Мир технологий (Technology World)
Вакансии 📌
https://t.me/sysadmin_rabota Системный Администратор
https://t.me/progjob Вакансии в IT
Telegram
Bash Советы
🚀 Секреты и советы по Bash
🔹 Полезные трюки, хитрые однострочники и лайфхаки для работы в терминале.
🔹 Автоматизация, скрипты и оптимизация работы в Linux.
🔹 Стать мастером Bash легко – просто подпишись!
По всем вопросам @evgenycarter
🔹 Полезные трюки, хитрые однострочники и лайфхаки для работы в терминале.
🔹 Автоматизация, скрипты и оптимизация работы в Linux.
🔹 Стать мастером Bash легко – просто подпишись!
По всем вопросам @evgenycarter
🔥1
📌 Ошибка: Вызов метода с
Почему? Spring оборачивает бин в прокси, а внутренние вызовы проходят мимо прокси, значит, аннотация игнорируется.
Пример проблемы:
💡 Как правильно:
1. Вынести
2. Или получить прокси текущего бина через
⚠️ Важный момент:
Понимание этой тонкости критично для корректного управления транзакциями в Spring! 🚀
👉@BookJava
@Transactional
внутри того же класса не запускает новую транзакцию.Почему? Spring оборачивает бин в прокси, а внутренние вызовы проходят мимо прокси, значит, аннотация игнорируется.
Пример проблемы:
@Service
public class OrderService {
@Transactional
public void createOrder() {
saveOrder();
}
@Transactional
public void saveOrder() {
// Новый транзакционный контекст не создастся!
}
}
💡 Как правильно:
1. Вынести
saveOrder()
в отдельный бин.2. Или получить прокси текущего бина через
AopContext
:
@Service
@EnableAspectJAutoProxy(exposeProxy = true) // важно!
public class OrderService {
@Transactional
public void createOrder() {
((OrderService) AopContext.currentProxy()).saveOrder();
}
@Transactional
public void saveOrder() {
// Теперь всё ок ✅
}
}
⚠️ Важный момент:
exposeProxy = true
нужен на уровне конфигурации, иначе AopContext
не заработает.Понимание этой тонкости критично для корректного управления транзакциями в Spring! 🚀
👉@BookJava
👍5👎3
🧠 Сегодня покажу вам полезный трюк для Spring Boot: как элегантно валидировать параметры контроллера без лишнего кода.
Часто вижу, как в контроллерах вручную проверяют
Вместо этого используйте аннотации валидации прямо на параметрах:
📌 Ключевые моменты:
- Обязательно ставим
- На параметры добавляем любые стандартные аннотации из
- Ошибки валидации Spring обработает автоматически через
💡 Можно кастомизировать ответ на ошибку, добавив глобальный
⚠️ Без
👉@BookJava
Часто вижу, как в контроллерах вручную проверяют
@RequestParam
на пустоту или формат. Это шумит код и приводит к ошибкам.Вместо этого используйте аннотации валидации прямо на параметрах:
@RestController
@RequestMapping("/api")
@Validated // Обязательно!
public class UserController {
@GetMapping("/users")
public List<User> getUsers(
@RequestParam @NotBlank String name,
@RequestParam @Min(18) int age
) {
// Если валидация не пройдена — автоматически вернётся 400 Bad Request
return userService.findUsers(name, age);
}
}
📌 Ключевые моменты:
- Обязательно ставим
@Validated
над классом контроллера.- На параметры добавляем любые стандартные аннотации из
jakarta.validation.constraints
.- Ошибки валидации Spring обработает автоматически через
MethodArgumentNotValidException
.💡 Можно кастомизировать ответ на ошибку, добавив глобальный
@ExceptionHandler
.⚠️ Без
@Validated
аннотации на контроллере валидация параметров работать не будет!👉@BookJava
👍10
Есть фича. Есть дедлайн. Есть понимание, что тесты надо писать… но они отъедают время, которого итак впритык.
Один наш знакомый девелопер сказал: «С тех пор как поставил Explyt Test — начал писать меньше тестов… но покрытие стало лучше. Как это вообще возможно?!»
Попробуйте сами. Плагин сам предлагает тесты для вашего кода — прямо в IDE.
👉 explyt.ai — сэкономь себе пару часов уже сегодня.
Один наш знакомый девелопер сказал: «С тех пор как поставил Explyt Test — начал писать меньше тестов… но покрытие стало лучше. Как это вообще возможно?!»
Попробуйте сами. Плагин сам предлагает тесты для вашего кода — прямо в IDE.
👉 explyt.ai — сэкономь себе пару часов уже сегодня.
👍4🔥1
В Java instance initializer blocks (блоки инициализации экземпляра) выполняются в следующем порядке:
- Они выполняются каждый раз, когда создается новый объект класса.
- Выполнение происходит после вызова конструктора родительского класса (
Порядок инициализации:
1. Сначала инициализируются поля в порядке их объявления.
2. Затем выполняются instance initializer blocks, в том порядке, в котором они написаны в коде.
3. После этого выполняется тело конструктора.
Пример:
Вывод:
Ключевые моменты:
- Статические блоки (
- Instance initializer blocks полезны для общей инициализации, которую нужно выполнять вне зависимости от того, какой конструктор вызывается.
👉@BookJava
- Они выполняются каждый раз, когда создается новый объект класса.
- Выполнение происходит после вызова конструктора родительского класса (
super()
), но до тела конструктора текущего класса.Порядок инициализации:
1. Сначала инициализируются поля в порядке их объявления.
2. Затем выполняются instance initializer blocks, в том порядке, в котором они написаны в коде.
3. После этого выполняется тело конструктора.
Пример:
class Example {
int x = 10;
{
System.out.println("Instance initializer block");
x = 20;
}
Example() {
System.out.println("Constructor");
System.out.println("x = " + x);
}
public static void main(String[] args) {
Example ex = new Example();
}
}
Вывод:
Instance initializer block
Constructor
x = 20
Ключевые моменты:
- Статические блоки (
static {}
) — другое дело: они выполняются один раз при загрузке класса.- Instance initializer blocks полезны для общей инициализации, которую нужно выполнять вне зависимости от того, какой конструктор вызывается.
👉@BookJava
👍7🔥3
Как работают instance initializer blocks.
Пример с родительским и дочерним классами:
Вывод программы:
Пошаговое выполнение:
1. Сначала загружается родительский класс
2. Выполняется:
- Инициализация полей родителя (
- Потом instance initializer блока родителя (
- Потом конструктор родителя (
3. Далее переходим к дочернему классу
- Инициализация полей дочернего класса (
- Потом instance initializer блока дочернего класса (
- Потом конструктор дочернего класса (
Важный порядок действий:
1. Инициализация родителя → 2. Конструктор родителя → 3. Инициализация потомка → 4. Конструктор потомка.
Блоки инициализации всегда выполняются до тела конструктора, но после вызова
👉@BookJava
Пример с родительским и дочерним классами:
class Parent {
int a = 5;
{
System.out.println("Parent instance initializer");
a = 10;
}
Parent() {
System.out.println("Parent constructor, a = " + a);
}
}
class Child extends Parent {
int b = 15;
{
System.out.println("Child instance initializer");
b = 25;
}
Child() {
System.out.println("Child constructor, b = " + b);
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
}
}
Вывод программы:
Parent instance initializer
Parent constructor, a = 10
Child instance initializer
Child constructor, b = 25
Пошаговое выполнение:
1. Сначала загружается родительский класс
Parent
.2. Выполняется:
- Инициализация полей родителя (
a = 5
),- Потом instance initializer блока родителя (
a = 10
),- Потом конструктор родителя (
Parent()
).3. Далее переходим к дочернему классу
Child
:- Инициализация полей дочернего класса (
b = 15
),- Потом instance initializer блока дочернего класса (
b = 25
),- Потом конструктор дочернего класса (
Child()
).Важный порядок действий:
1. Инициализация родителя → 2. Конструктор родителя → 3. Инициализация потомка → 4. Конструктор потомка.
Блоки инициализации всегда выполняются до тела конструктора, но после вызова
super()
.👉@BookJava
👍6
💡Сегодня покажу крутую фишку для оптимизации чтения больших коллекций из базы через JPA.
📌 Проблема:
Когда загружаем большую коллекцию через
Это может привести к OutOfMemoryError или резкому проседанию производительности.
📌 Решение: использовать пагинацию (
Как настроить
🧠 Теперь Hibernate будет загружать за раз по 50 элементов, а не всю коллекцию сразу!
📌 Или можно настроить глобально через
⚠️ Важно:
-
- Это не пагинация в SQL, а оптимизация внутренних запросов Hibernate.
- Если коллекция огромная (100k+ записей) — лучше делать явные
💡Помните: без настройки
👉@BookJava
📌 Проблема:
Когда загружаем большую коллекцию через
@OneToMany
, Hibernate часто делает это лениво (LAZY
), но при первом доступе — забирает всю коллекцию целиком. Это может привести к OutOfMemoryError или резкому проседанию производительности.
📌 Решение: использовать пагинацию (
batch-size
) или запрос коллекции порциями.Как настроить
batch-size
на уровне сущности:
@Entity
public class User {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@BatchSize(size = 50)
private List<Order> orders;
}
🧠 Теперь Hibernate будет загружать за раз по 50 элементов, а не всю коллекцию сразу!
📌 Или можно настроить глобально через
application.properties
:
spring.jpa.properties.hibernate.default_batch_fetch_size=50
⚠️ Важно:
-
@BatchSize
работает только для LAZY-связей.- Это не пагинация в SQL, а оптимизация внутренних запросов Hibernate.
- Если коллекция огромная (100k+ записей) — лучше делать явные
paged
запросы в репозитории.💡Помните: без настройки
batch-size
Hibernate может сломать приложение под нагрузкой. Оптимизируйте загрузку коллекций заранее!👉@BookJava
🔥6👍3❤1
🤖 А ты справишься с тестом по Kotlin?
🏆 Пройди тест из 10 вопросов, проверь свой уровень знаний и получи скидку на онлайн-курс «Kotlin Backend Developer. Professional» от OTUS!
Если успешно пройдешь тест, сможешь забронировать место в группе по выгодной цене! И еще дарим промокодKotlin5
🎫 Курс можно приобрести в рассрочку
➡️ Пройти тест и забрать скидку: https://vk.cc/cLkRLn
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🏆 Пройди тест из 10 вопросов, проверь свой уровень знаний и получи скидку на онлайн-курс «Kotlin Backend Developer. Professional» от OTUS!
Если успешно пройдешь тест, сможешь забронировать место в группе по выгодной цене! И еще дарим промокод
🎫 Курс можно приобрести в рассрочку
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 Как в Java сломать equals() и потерять данные в HashMap
Сегодня покажу, как неочевидный баг в
Допустим, у вас есть Entity:
Вроде норм. Но теперь добавим такого юзера в
📌 Почему так происходит?
💡 Совет: если вы используете объект как ключ в мапе или добавляете его в Set, не изменяйте его поля, участвующие в
📌 А как правильно?
- Делайте такие поля
- Или используйте неизменяемые типы (
- Или не используйте такие объекты как ключи вовсе.
Вот безопасный вариант с
👉@BookJava
Сегодня покажу, как неочевидный баг в
equals()/hashCode()
может привести к потере данных при работе с HashMap
.Допустим, у вас есть Entity:
public class User {
private String id;
private String name;
// equals/hashCode только по id
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Вроде норм. Но теперь добавим такого юзера в
HashMap
, а потом... изменим его id:
User user = new User();
user.setId("1");
user.setName("Alice");
Map<User, String> map = new HashMap<>();
map.put(user, "value");
user.setId("2"); // ⚠️ ключ стал "невидимым"
System.out.println(map.get(user)); // null 😱
📌 Почему так происходит?
HashMap
ищет ключ по hashCode()
→ ищет бакет → сравнивает через equals()
. А hashCode()
уже другой, и объект "теряется".💡 Совет: если вы используете объект как ключ в мапе или добавляете его в Set, не изменяйте его поля, участвующие в
equals()
/hashCode()
!📌 А как правильно?
- Делайте такие поля
final
;- Или используйте неизменяемые типы (
record
);- Или не используйте такие объекты как ключи вовсе.
Вот безопасный вариант с
record
:
public record User(String id, String name) {}
👉@BookJava
👍5
Java. Сортировки
Java. Сортировка пузырьком.
Java. О сортировке выбором.
Java. Быстрая сортировка. Объяснение на пальцах)
Java. Оценка сложности алгоритмов сортировки.
Java. Сортировка слиянием.
Java. Сортировка подсчетом.
Java. Сортировка вставками.
Java. Сортировка расческой. От пузырька до расчески.
👉@BookJava
Java. Сортировка пузырьком.
Java. О сортировке выбором.
Java. Быстрая сортировка. Объяснение на пальцах)
Java. Оценка сложности алгоритмов сортировки.
Java. Сортировка слиянием.
Java. Сортировка подсчетом.
Java. Сортировка вставками.
Java. Сортировка расческой. От пузырька до расчески.
👉@BookJava
👍4