Fury Java
219 subscribers
16 photos
3 links
Красим джейсоны и повышаем надои с яростной джавой 🤗
Реальные кейсы из практики, задачи с собесов, теоретические нюансы

🫨 никаких бесполезных мемов
🥱 никаких тупых постов из-под chatGPT

Чат: https://t.me/fury_java_chat
Автор: https://t.me/Ldv236
Download Telegram
Как стримы дружат с мапами, часть 2.
Тренируемся на кошках
.


3️⃣ Разбить объекты на две партиции по некоторому условию. Ключами являются в этом случае значение true и false, а объекты также собираются в коллекции (в одну - удовлетворяющие условию, в другую - остальные)
Map<Boolean, List<Kitty>> partitionedByYear = kitties.stream()
.collect(Collectors.partitioningBy(
kitty -> kitty.getYear() >= 2020 // любое условие
));

После этого по ключу true получаем список кошек, у которых поле "year" больше или равно 2020, по ключу false - от 2019 и меньше.

4️⃣ Так как самым популярным является первый вариант, то можно рассмотреть для него еще некоторые дополнительные возможности.
🎱 Например, что делать если при использовании метода toMap() ключи могут совпадать?
С помощью третьего параметра можно сказать стриму оставить первое найденное с этим ключом value, а можно - последнее.
И даже! - сказать ему вообще убрать из мапы такие объекты, у которых ключи оказались повторяющимися:
Map<String, Kitty> kittyById = kitties.stream()
.collect(Collectors.toMap(
Kitty::getName, // например, у кошек одинаковые имена
Function.identity(),
(oldValue, newValue) -> oldValue // оставляем первую такую кошку
// или
(oldValue, newValue) -> newValue // оставляем последнюю (новую)
// или
(oldValue, newValue) -> null // избавляемся от неуникальных
));

Естественно, oldValue и newValue - просто условные названия, их можно заменить на что угодно.
И еще - если не задать один из этих вариантов поведения, то при обнаружении одинаковых ключей выпадет исключение IllegalStateException: Duplicate key

🗺 Также в методе toMap() можно получить на выходе любой вид мапы:
Map<String, Kitty> kittyById = kitties.stream()
.collect(Collectors.toMap(
Kitty::getName, // ключи - имена
Function.identity(),
(old, new) -> old,
TreeMap::new // сразу сортируем по именам
));


Вывод:
в стримах есть много не очень часто используемых, но очень интересных возможностей, мы увидели это на примере формирования мапы на основе списка объектов

P.S. Ни одна kitty не пострадала
1👍64🔥4
Fury Java pinned «Иногда в пылу разработки мы забиваем забываем об одной важной детали при работе с реляционными БД — индексах на внешних ключах 🔍Казалось бы, мелочь: зачем заморачиваться с ещё одним индексом, если сам внешний ключ уже прописан? Но именно индекс по FK способен…»
Про паттерны и собеседования (и про тот-паттерн-который-нельзя-называть)

На собеседованиях часто спрашивают про паттерны (что-то вроде «Расскажи про паттерны. Какие знаешь?»).
Хочется сразу сделать несколько оговорок:

1️⃣ когда говорят "паттерны", обычно подразумевают паттерны проектирования или Design Patterns - из известной книги "банды четырёх". Однако, паттерны – более широкое понятие. Есть паттерны микросервисной архитектуры, паттерны concurrency (вроде Thread Pool), паттерны интеграции (например, Circuit Breaker)
даже банальный DTO - тоже паттерн

(кстати, описан Фаулером в книге Patterns of Enterprise Application Architecture), т.е. можно сказать, что это архитектурный или же прикладной паттерн, в отличие в паттернов банды четырех, которые сфокусированы на объектно-ориентированном проектировании


2️⃣ у "банды четырёх" 23 паттерна, но не все из них часто используются (возможно, некоторые даже не выходили за пределы страниц книги 😳)

3️⃣ точнее - немногие из них программисту приходится реализовывать руками, потому что они используются под капотом во фреймворках и готовых библиотеках.
Например, Dependency Injection или Proxy в спринге — это уже готовые решения. Про такие надо знать, даже если не писал их ни разу самостоятельно, чтобы понимать, как оно работает внутри, и какие могут быть подводные камни

4️⃣ и последнее: паттерны — не rocket science, и при некотором упорстве многие из них можно изобрести самостоятельно, задавшись целью оптимизировать свой код. Но зачем, если их уже придумали за нас?

В общем-то, можно поделиться с собеседующим подобными размышлениями, и это будет неплохое начало ответа. А после добавить конкретики и — ВАЖНО — личного опыта работы с паттернами.
➡️Ссылаться на личный опыт — в целом выигрышный приём при ответе на любой вопрос. Это покажет, что вы не просто запомнили нужные слова, а действительно понимаете, о чём говорите.
Сделайте это своим паттерном прохождения технических интервью!


Так вот, после этого надо перейти к конкретике…
а конкретика в следующем посте

👻 Но главное – никогда не говорите про паттерн «синглтон»
Просто не произносите это слово вслух, поберегите себя
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥13👍4👏1
Обещанное продолжение про паттерны.
В прошлом посте были рассуждения о паттернах в целом, которыми можно поделиться на собеседовании, чтобы все поняли какой вы пирожочек. А теперь конкретика:
и да – представляем, что разговор идет в контексте ООП-design patterns от банды четырех


Паттерны делятся на три вида по их назначению:
1️⃣Порождающие (Creational) — решают, как создавать объекты. Например, Factory или Builder
2️⃣Структурные (Structural) — помогают выстраивать структуру системы. Adapter, Decorator - про то, как объекты и классы "складываются" вместе
3️⃣Поведенческие (Behavioral) — отвечают за взаимодействие объектов и разделение обязанностей. Strategy, Observer, Command — про поведение и логику

В Спринге паттерны встречаются повсюду:
➡️Factory (порождающий) — BeanFactory или ApplicationContext.
➡️Proxy (структурный) — используется для аспектов, транзакций
➡️Chain of Responsibility (поведенческий) — фильтры в Spring Security
➡️Builder (порождающий) — например, в RestTemplateBuilder

🤔 Что касается реализации паттернов руками (добавляете личный опыт к сухой теории) - недавно делал(а) Стратегию - это поведенческий паттерн для формирования семейства алгоритмов. Ситуация такая: есть объект, и некоторая операция над ним может осуществляться по-разному в зависимости от какого-то его параметра.
Например, обработка юзера отличается в зависимости от его типа (обычный или премиум) – тут приведите пример из своей предметной области

Можно было сделать через switch-case или if-else, но метод бы раздулся, да и новые варианты, которые точно будут в будущем добавляться, внедрять неудобно (обоснование, почему применение паттерна было целесообразно)

Как именно реализовал стратегию:
1️⃣ Написал интерфейс с одним методом,
например process, означающим ту самую вариативную операцию (метод принимает самого юзера, с которым надо работать, тут для себя вспомните про передачу объектов в джаве – в методе можно изменять существующий объект, пусть void не смущает):

public interface UserStrategy {
void process(User user);
}


2️⃣ Написал его реализации (имплементации)
(количество реализаций равно количеству вариантов бизнес-логики операции, в этом случае 2 - для обычных и для премиум юзеров):

public class RegularUserStrategy implements UserStrategy {
@Override
public void process(User user) {
// Логика обработки обычного юзера
}
}

public class PremiumUserStrategy implements UserStrategy {
@Override
public void process(User user) {
// Логика обработки премиум юзера с плюшками
}
}


3️⃣ Как это использовать?
В UserProcessor (или там где у нас выполняется операция над юзером) внедряем интерфейс UserStrategy как поле класса. А раз у этого интерфейса несколько реализаций, то можно передать любую из них (= инициализировать поле strategy объектом любого из этих классов, привет полиморфизм).
После для strategy вызываем тот самый метод process (внутри метода execute), и отрабатывает нужный нам вариант логики (т.е. переопределенный метод из конкретной реализации)

public class UserProcessor {
private final UserStrategy strategy;

public UserProcessor(UserStrategy strategy) {
this.strategy = strategy;
}
public void execute(User user) {
strategy.process(user);
}
}


4️⃣ Ну и использовал в логике приложения
Есть юзер Иван, а у него поле со значением "premium". Иван мог бы быть и обычным юзером, но раз он премиум, то передаем в конструктор UserProcessor-а обработчик PremiumUserStrategy, далее передаем в метод execute Ивана

User user = new User(“Иван”, “premium”);
UserProcessor processor = new UserProcessor(new PremiumUserStrategy());
processor.execute(user);


🥳 Получился нормальный ответ про паттерны.
Напомню, что пример вам нужно выбрать из вашего домена и приложения, а чтобы рассказывать об этом уверенно, надо действительно сделать это своими руками.


❗️ Но в реальности, как правило, мы пишем приложения на Спринге, поэтому позже расскажу как реализовать такую схему с помощью спрингового DI (dependency injection), там просто кайф. Тогда картина станет полной
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥133👏2
В последней Intellij Idea 2024.3.4 появилась классная фича -
логическая структура проекта.

Нет, там много классных фич, на эта стоит прям на первом месте под заголовком "Главное".
Касается панели "Structure", которая раньше была так себе, а теперь прям вау.
Не буду утомлять текстом, смотрите скрины на примере простенького проекта одного из моих менти (скрин собственно из "What`s New in IntelliJ IDEA" там же в конце)
6👍13🔥6🤔21👏1
Про Стратегию в Spring-приложении.
В этом посте писал о том, что в реальности Стратегия чаще применяется не совсем так, как это обычно показывается в книгах по ООП-паттернам.
Вдруг понял, что в тексте это сложно преподнести так, как я хочу, поэтому в творческом кураже запилил первое на канале видео:
📺 Youtube

📺 Rutube

📺 ВК-видео


Код на гитхабе - ReportStrategy

Примечание: в видео не всё учтено (не судите строго, хотя нет - судите, можете даже писать гневные комменты, и чем больше - тем лучше🤔), так что в репозиторий внесены некоторые дополнения с комментариями:
➡️ поля в стратегиях объявлены как final
➡️ добавлена валидация списка стратегий во избежание NPE в рантайме
➡️ используется EnumMap
Please open Telegram to view this post
VIEW IN TELEGRAM
7🔥11👍842
Клиент -> запрос -> сервер. Давайте попробуем "сказать людям" 😄 что веб-разработка всё же несколько сложнее, чем на этой картинке, и начнём с малого

По стрелке request данные от клиента к серверу (клиент-серверная архитектура же) могут приходить разными способами.
Здесь составлю короткую памятку по аннотациям спринга для этих способов

1. @PathVariable

➡️ берём часть пути (например, /users/{id})
➡️ удобно, когда нужно получить динамический сегмент URL (обычно ID сущностей)

2. @RequestParam

➡️ берём Query-параметры (GET url?param=value) - они тоже в урле, как и @PathVariable, но не являются частью пути, а добавляются в хвосте через знак вопроса
➡️ если параметров несколько, они разделяются амперсандом &
➡️ такие параметры могут быть обязательными или нет (required=false)
➡️ используется для простых атомарных параметров, например, для фильтрации (указываются условия для выборочного возвращения каких-то запрашиваемых объектов)

3. @RequestBody

➡️ берём JSON/XML из тела запроса (обращайте внимание на header запроса Content-Type - application/json или application/xml)
➡️ для сложных объектов. Spring автоматически преобразует данные в DTO или другой объект, указанный как параметр метода контроллера

4. @ModelAttribute

➡️ берём query-параметры и собираем из них объект (DTO).
Т.е. данные передаются как при @RequestParam, но мы не хотим использовать их по-отдельности. Конечно, в объекте должны быть поля с теми же именами, что и передаваемые параметры
➡️ часто используется для пагинации и сортировки
(тут есть хитрость для формирования документации вашего восхитительного api в сваггере - чтобы поля этого объекта отображались как отдельные параметры, нужно применить рядом @ParameterObject, но работать будет и без неё)
➡️ если вообще не указать аннотацию перед объектом, то формирование этого объекта будет такое же, как с аннотацией @ModelAttribute, т.е. можно сказать, что она используется по умолчанию
➡️ также применяется для приёма данных в виде "multipart/form-data", если нужно передавать параметры и сразу файлы (загляните в постман, там есть такой вариант в разделе Body, указываются пары Key-Value с типом text/file)

5. @RequestPart

➡️ берём отдельные части из multipart/form-data (а не собираем в объект как с @ModelAttribute)
➡️ полезно для загрузки файлов

6. @RequestHeader

➡️ берём заголовки из запроса (например, User-Agent, Authorization)

7. @CookieValue

➡️ берём куки. Зачем? Потому что можем

🤔🤔🤔
И напоследок еще один каверзный вопрос из реальных собеседований -
можно ли передавать тело в get-запросе? 🤔
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍83🔥3👏1👌1
Неочевидное.

Что первое отвечает джавист на вопрос "для чего переопределять в классах методы equals и hashCode?".
➡️ Чтобы корректно сравнивать объекты, но для этого достаточно equals, а hashCode - чтобы использовать экземпляры этих классов как ключи в хэшмапе (на то она и "хэш-"). Также между методами equals и hashCode предусмотрен контракт, чтобы это всё правильно работало.

🤔 Если отвечаете как-то иначе - прошу в комменты.

Но дальше может возникнуть вопрос - а разве мы так часто используем мапы, в которых ключи - наши объекты? Обычно же в ключах используем строки или числа, не? 😨

И вот тут надо немного углубиться в Java Collection Framework (JCF).
На обычной схеме иерархии коллекций мы видим две отдельные ветки - по Map и по Set, и между ними не обозначено никаких связей . Но давайте зайдем в класс HashSet и посмотрим на банальную штуку - конструктор
public HashSet() {
map = new HashMap<>();
}

Это что же получается - "нет никакого Сета, это всё компьютерная графика"? Получается, да. Сет - это замаскированная ХэшМапа. Которая сохранена в переменную с говорящим названием map:
transient HashMap<E,Object> map;


А метод добавления элемента в Сет выглядит как добавление в мапу простым put:
public void add(E e) {
map.put(e, PRESENT);
}

Но есть нюанс: элемент e вставляется вместо ключа (первый параметр в put), а вместо значения - какой-то PRESENT.
А PRESENT - это просто константа-заглушка, dummy. Просто один самый простой объект Object, который используется во всех values внутренней ХэшМапы:
// Dummy value to associate with an Object in the backing Map
static final Object PRESENT = new Object();


Итак, из двух обозначенных утверждений
1️⃣ - иквалс и хэшкод нужны для ключей ХэшМапы
2️⃣ - ХэшСет внутри является ХэшМапой и сохраняет элементы как ключи этой внутренней ХэшМапы

мы можем сделать третье утверждение:

🤌 для элементов Сета корректность переопределения методов equals и hashCode так же важна, как и для ключей ХэшМапы

(при этом использование ХэшСета с нашими собственными объектами - не такая уж и редкость).

Только теперь непонятно 🤔 почему на схеме-то этого не показано?
- во-первых, там показано наследование и реализация интерфейсов, а Сет и Мап связаны по-другому, скорее композиционно;
- во-вторых, на этой схеме вообще много чего не показано, она очень упрощена!
Конец.


Хммм, а если есть не только HashSet, а еще LinkedHashSet и TreeSet, в них тоже внутри используется HashMap или...
Но об этом в другом посте
Please open Telegram to view this post
VIEW IN TELEGRAM
3🔥13👍622👨‍💻2
Есть три сета, которые нужно объединить:
Set<String> set1 = Set.of("A1", "A2", ..., "A1000");
Set<String> set2 = Set.of("B1", "B2", ..., "B1000");
Set<String> set3 = Set.of("C1", "C2", ..., "C1000");


Как бы вы это сделали? (тык в опрос ниже + горячо приветствуется обсуждение)

1)
Set<String> result = new HashSet<>();
result.addAll(set1);
result.addAll(set2);
result.addAll(set3);


2)
Set<String> result = Stream.of(set1, set2, set3)
.flatMap(Set::stream)
.collect(Collectors.toSet());


3) другим способом (или одним из указанных, но доработанным)
pu pu pu
2🤔6👍3🤓2💅1