🟡 Дайджест полезных материалов из мира java за неделю
Почитать:
— Поднимаем кластер PostgreSQL в Docker и Testcontainers
— Исчерпывающее руководство по одномерным массивам в Java
— Новый взгляд на Maven-plugin для IDEA — GMaven
— Kafka UI краткий гайд
— Пошушукаемся о Барбаре Лисков или раз и навсегда запоминаем принцип подстановки
— Список популярных утечек с GitHub: Анализ репозиториев компаний
— Java Digest #3
— Из кладовщика в разработчики
— Использование Kotlin и WebFlux для выполнения задач ML в Apache Spark на GPU
— Свойство типа Controller Service в кастомном процессоре NiFi
— Опыт Сдачи: Oracle Certified Professional: Java SE 11 Developer 1Z0-819
— Get URL pointed Resource Information in Java
— GPT me liste 10 exercicios...
— How to Install JDK for Debian using ARM64: Fixing 'dpkg: error: archive 'jdk-20.0.2' is not a regular file'
— 3 Ways to Make Your API Beautiful
— A beginner-friendly design patterns masterclass
— Product of Array Except Self - LeetCode Java Solution
— Two Sum — LeetCode Java Solution
— Valid Anagram - LeetCode Java Solution
— Contains Duplicate - LeetCode Java Solution
— In Java ein Word-Dokument in ein verschlüsseltes PDF-Dokument umwandeln
Посмотреть:
🌐Why They Called It Java | Prime Reacts
Хорошего дня!
❤️ Лайк, если полезно
@javatg
Почитать:
— Поднимаем кластер PostgreSQL в Docker и Testcontainers
— Исчерпывающее руководство по одномерным массивам в Java
— Новый взгляд на Maven-plugin для IDEA — GMaven
— Kafka UI краткий гайд
— Пошушукаемся о Барбаре Лисков или раз и навсегда запоминаем принцип подстановки
— Список популярных утечек с GitHub: Анализ репозиториев компаний
— Java Digest #3
— Из кладовщика в разработчики
— Использование Kotlin и WebFlux для выполнения задач ML в Apache Spark на GPU
— Свойство типа Controller Service в кастомном процессоре NiFi
— Опыт Сдачи: Oracle Certified Professional: Java SE 11 Developer 1Z0-819
— Get URL pointed Resource Information in Java
— GPT me liste 10 exercicios...
— How to Install JDK for Debian using ARM64: Fixing 'dpkg: error: archive 'jdk-20.0.2' is not a regular file'
— 3 Ways to Make Your API Beautiful
— A beginner-friendly design patterns masterclass
— Product of Array Except Self - LeetCode Java Solution
— Two Sum — LeetCode Java Solution
— Valid Anagram - LeetCode Java Solution
— Contains Duplicate - LeetCode Java Solution
— In Java ein Word-Dokument in ein verschlüsseltes PDF-Dokument umwandeln
Посмотреть:
🌐Why They Called It Java | Prime Reacts
Хорошего дня!
❤️ Лайк, если полезно
@javatg
Вопросы-ответы собеседования
1. Что такое статический блок инициализации? static инициализатор
2. Что такое статический вложенный класс? static nested class
3. Какие типы ссылок есть в Java? Что такое Strong, Soft, Weak, Phantom Reference?
4. Что такое Strong Reference? Как работает сильная ссылка?
5. Что такое Soft Reference? Как работает мягкая ссылка?
6. Что такое Weak Reference? Как работает слабая ссылка?
7. Что такое Phantom Reference? Как работает фантомная ссылка?
8. Чем отличается интерфейс от абстрактного класса? Что использовать?
9. Что такое String Pool? Как работает пул строк?
10. Что такое пул строк? Изучаем String Pool за 50 секунд!
#video #java
https://www.youtube.com/watch?v=onDWBO2CYKY&list=PLvalLeYySoeC9i2KLvq1af7P6CllrsBsm&ab_channel=Javangelion%7C%D0%94%D0%B6%D0%B0%D0%B2%D0%B0%D0%BD%D0%B3%D0%B5%D0%BB%D0%B8%D0%BE%D0%BD
@javatg
1. Что такое статический блок инициализации? static инициализатор
2. Что такое статический вложенный класс? static nested class
3. Какие типы ссылок есть в Java? Что такое Strong, Soft, Weak, Phantom Reference?
4. Что такое Strong Reference? Как работает сильная ссылка?
5. Что такое Soft Reference? Как работает мягкая ссылка?
6. Что такое Weak Reference? Как работает слабая ссылка?
7. Что такое Phantom Reference? Как работает фантомная ссылка?
8. Чем отличается интерфейс от абстрактного класса? Что использовать?
9. Что такое String Pool? Как работает пул строк?
10. Что такое пул строк? Изучаем String Pool за 50 секунд!
#video #java
https://www.youtube.com/watch?v=onDWBO2CYKY&list=PLvalLeYySoeC9i2KLvq1af7P6CllrsBsm&ab_channel=Javangelion%7C%D0%94%D0%B6%D0%B0%D0%B2%D0%B0%D0%BD%D0%B3%D0%B5%D0%BB%D0%B8%D0%BE%D0%BD
@javatg
Выпуск версии Java 8 стал знаменательным событием в истории Java. В нем были представлены потоки данных (англ. Streams) и лямбда-выражения, которые сейчас широко применяются. Если вы не знакомы с потоками данных или никогда не слышали о них, то ничего страшного. В большинстве случаев можно обойтись без них, задействуя циклы.
И зачем тогда, спрашивается, нужны потоки данных? Есть ли у них преимущества перед циклами? Могут ли они их заменить? В статье мы изучим соответствующий код, сравним производительность и посмотрим, смогут ли потоки данных стать полноценной заменой циклов.
Сравнение кода
Потоки данных увеличивают сложность кода, поскольку им нужны классы, интерфейсы и импорт. В отличие от них, циклы изначально встроены. В каких-то случаях это действительно так, но не всегда. Сложность кода не сводится только к объемам требуемых знаний для его понимания. В большей мере она определяется степенью читаемости кода. Обратимся к примерам.
Список имен элементов с заданным типом
Допустим, у нас есть список элементов, и нужно получить список имен элементов с заданным типом. Используя циклы, пишем следующий код:
List<String> getItemNamesOfType(List<Item> items, Item.Type type) {
List<String> itemNames = new ArrayList<>();
for (Item item : items) {
if (item.type() == type) {
itemNames.add(item.name());
}
}
return itemNames;
}
Читаем код и видим, что требуется создать новый ArrayList и в каждом цикле выполнять проверку типов и вызов add(). Теперь посмотрим, как с этой же задачей справляется поток данных:
List<String> getItemNamesOfTypeStream(List<Item> items, Item.Type type) {
return items.stream()
.filter(item -> item.type() == type)
.map(item -> item.name())
.toList();
}
С помощью лямбда-выражения сразу становится понятно, что мы сначала выбираем элементы с заданным типом, а затем получаем список имен отфильтрованных элементов. В таком варианте кода построчный поток хорошо согласуется с логическим потоком.
Генерация случайного списка
Переходим к другому примеру. В следующих разделах мы рассмотрим ключевые методы потоков данных и сравним время их выполнения с циклами. Для этого потребуется случайный список Item. Ниже представлен фрагмент кода со статическим методом, который генерирует такой список:
public record Item(Type type, String name) {
public enum Type {
WEAPON, ARMOR, HELMET, GLOVES, BOOTS,
}
private static final Random random = new Random();
private static final String[] NAMES = {
"beginner",
"knight",
"king",
"dragon",
};
public static Item random() {
return new Item(
Type.values()[random.nextInt(Type.values().length)],
NAMES[random.nextInt(NAMES.length)]);
}
}
Теперь создаем случайный список Item с помощью циклов. Пишем следующий код:
List<Item> items = new ArrayList<>(100);
for (int i = 0; i < 100; i++) {
items.add(Item.random());
}
Код с потоками данных выглядит следующим образом:
List<Item> items = Stream.generate(Item::random).limit(length).toList();
Превосходный и легко читаемый код. Более того, List, возвращаемый методом toList(), представляет собой неизменяемый список. Так что вы можете пользоваться неизменяемостью List и размещать его в любых местах кода, не беспокоясь о побочных эффектах. Такой подход снижает вероятность появления ошибок в коде и облегчает его понимание.
Потоки данных предоставляют множество полезных методов, способствующих написанию лаконичного кода. Перечислим самые востребованные из них:
allMatch();
anyMatch();
count();
filter();
findFirst();
forEach();
map();
reduce();
sorted();
limit()
и многие другие методы, с описанием которых можно ознакомиться по ссылке на документацию Stream Javadoc.
Производительность
При обычных обстоятельствах потоки данных ведут себя как циклы и практически не влияют на время выполнения. Сравним основные варианты поведения потоков с реализацией циклов.
📌Читать дальше
@javatg
Please open Telegram to view this post
VIEW IN TELEGRAM
ТОП 20 ТЕСТОВЫХ ЗАДАНИЙ НА ИНТЕРВЬЮ ДЛЯ JAVA РАЗРАБОТЧИКА
https://itvdn.com/ru/blog/article/test-java-20#17
@javatg
https://itvdn.com/ru/blog/article/test-java-20#17
@javatg
ITVDN
ТОП 20 тестовых заданий на интервью для Java разработчика
В этой статье мы приведем достаточно большое количество примеров программ из тех, что просят написать соискателей во время прохождения интервью на вакансию Java разработчика. Указанные тестовые задания мы приводим с реальными примерами кода, заданными в…
🟡 Дайджест полезных материалов из мира: Java за неделю
Почитать:
— Как скомпилировать Spring Boot приложение в native image с помощью GraalVm и развернуть его с помощью Docker
— Объяснения по шардинга баз данных
— Синхронизация в Java, часть 1: состояния гонки, блокировки и условия
— Как задеплоить монолитный pet проект на Java с 0 и не сойти с ума… Часть 1
— Авторизация в Spring Boot приложении с Spring Security 3.1.0 и PostgreSQL
— Ракету пустил и забыл. Или как заставить DI работать
— Поднимаем кластер PostgreSQL в Docker и Testcontainers
— Meeting & Mastering Java
— Navigating Code Testing Waters with Mockito: Your Testing Crew for Smooth Sailing
— I'm Learning Java s̶c̶r̶i̶p̶t̶
— Journey of completing Oracle Java Certification OCA 1Z0-808 exam
— Object Oriented Programming (OOP)
— A Deep Dive into Parking Spot LLD Design
— Difference between Interface and Abstract Class
— I’m continuing jDOSBox
— Mastering Background Processing in Android with WorkManager: A Guide to Efficient Background Processing
— [Digest] Java, Kotlin, Spring, Cloud, and More - Episode 8
Посмотреть:
🌐 Тестовое собеседование Java разработчика #28 - Леонид Земенков
Хорошего дня!
❤️ Лайк, за подборку
@javatg
Почитать:
— Как скомпилировать Spring Boot приложение в native image с помощью GraalVm и развернуть его с помощью Docker
— Объяснения по шардинга баз данных
— Синхронизация в Java, часть 1: состояния гонки, блокировки и условия
— Как задеплоить монолитный pet проект на Java с 0 и не сойти с ума… Часть 1
— Авторизация в Spring Boot приложении с Spring Security 3.1.0 и PostgreSQL
— Ракету пустил и забыл. Или как заставить DI работать
— Поднимаем кластер PostgreSQL в Docker и Testcontainers
— Meeting & Mastering Java
— Navigating Code Testing Waters with Mockito: Your Testing Crew for Smooth Sailing
— I'm Learning Java s̶c̶r̶i̶p̶t̶
— Journey of completing Oracle Java Certification OCA 1Z0-808 exam
— Object Oriented Programming (OOP)
— A Deep Dive into Parking Spot LLD Design
— Difference between Interface and Abstract Class
— I’m continuing jDOSBox
— Mastering Background Processing in Android with WorkManager: A Guide to Efficient Background Processing
— [Digest] Java, Kotlin, Spring, Cloud, and More - Episode 8
Посмотреть:
🌐 Тестовое собеседование Java разработчика #28 - Леонид Земенков
Хорошего дня!
❤️ Лайк, за подборку
@javatg
⚡Легкий способ получать свежие обновлении и следить за трендами в разработке на вашем языке. Находите свой стек и подписывайтесь:
Машинное обучение: @ai_machinelearning_big_data
Go: @Golang_google
C#: @csharp_ci
Базы данных: @sqlhub
Python: @python_job_interview
C/C++/: @cpluspluc
Data Science: @data_analysis_ml
Devops: @devOPSitsec
Rust: @rust_code
Javascript: @javascriptv
React: @react_tg
PHP: @phpshka
Docker: @docker
Android: @android_its
Мобильная разработка: @mobdevelop
Linux: linuxacademy
Big Data: www.tg-me.com/bigdatai
Хакинг: @linuxkalii
Java:@javatg
Собеседования: @machinelearning_interview
💼 Папка с вакансиями: www.tg-me.com/addlist/_zyy_jQ_QUsyM2Vi
Папка Go разработчика: www.tg-me.com/addlist/MUtJEeJSxeY2YTFi
Папка Python разработчика: www.tg-me.com/addlist/eEPya-HF6mkxMGIy
🔥ИТ-Мемы: www.tg-me.com/memes_prog
🇬🇧Английский: @english_forprogrammers
Машинное обучение: @ai_machinelearning_big_data
Go: @Golang_google
C#: @csharp_ci
Базы данных: @sqlhub
Python: @python_job_interview
C/C++/: @cpluspluc
Data Science: @data_analysis_ml
Devops: @devOPSitsec
Rust: @rust_code
Javascript: @javascriptv
React: @react_tg
PHP: @phpshka
Docker: @docker
Android: @android_its
Мобильная разработка: @mobdevelop
Linux: linuxacademy
Big Data: www.tg-me.com/bigdatai
Хакинг: @linuxkalii
Java:@javatg
Собеседования: @machinelearning_interview
💼 Папка с вакансиями: www.tg-me.com/addlist/_zyy_jQ_QUsyM2Vi
Папка Go разработчика: www.tg-me.com/addlist/MUtJEeJSxeY2YTFi
Папка Python разработчика: www.tg-me.com/addlist/eEPya-HF6mkxMGIy
🔥ИТ-Мемы: www.tg-me.com/memes_prog
🇬🇧Английский: @english_forprogrammers
1. JavaШаблоны проектирвоания
2. Cобеседования
3. Алгоритмы
4. Проекты
5. Awesome Java
6. Baeldung Tutorials
7. JHipster
8. Spring Framework
9. Spring Boot Tutorials by Java Guides
10. сracking the Coding Interview in Java
@javatg
Please open Telegram to view this post
VIEW IN TELEGRAM
📖 Фундаментальное руководство по пакетам в Java
• Пакеты в Java появились с самого начала, во времена, когда язык еще назывался Oak. Их описание уже присутствует в его ранних спецификациях.
• Java-пакеты реализуют так называемое пространство имен (namespace), позволяющее использовать в проекте файлы с одинаковыми именами. Такой подход существует с давних времен во многих языках.
Также с помощью пакетов разработчики структурируют файлы удобным и понятным для себя способом, т. к. чем их больше используется в программе, тем в них сложнее становится ориентироваться. Если они будут размещаться бессистемно, то это рано или поздно приведет к полному запутыванию кодовой базы и дезориентации программистов.
И наконец, с помощью пакетов можно ограничивать доступ (из других пакетов) как целиком к классам, так и к их отдельным элементам.
Об этом и многом другом и пойдет речь в данной статье.
1. Что такое пакет
2. Примеры пакетов
3. Назначение пакетов
4. Правила создания пакетов
5. Импорт классов
6. Компиляция и запуск
7. Возможные ошибки и их решение
📌 Читать
@javatg
• Пакеты в Java появились с самого начала, во времена, когда язык еще назывался Oak. Их описание уже присутствует в его ранних спецификациях.
• Java-пакеты реализуют так называемое пространство имен (namespace), позволяющее использовать в проекте файлы с одинаковыми именами. Такой подход существует с давних времен во многих языках.
Также с помощью пакетов разработчики структурируют файлы удобным и понятным для себя способом, т. к. чем их больше используется в программе, тем в них сложнее становится ориентироваться. Если они будут размещаться бессистемно, то это рано или поздно приведет к полному запутыванию кодовой базы и дезориентации программистов.
И наконец, с помощью пакетов можно ограничивать доступ (из других пакетов) как целиком к классам, так и к их отдельным элементам.
Об этом и многом другом и пойдет речь в данной статье.
1. Что такое пакет
2. Примеры пакетов
3. Назначение пакетов
4. Правила создания пакетов
5. Импорт классов
6. Компиляция и запуск
7. Возможные ошибки и их решение
📌 Читать
@javatg
NetMock — это мощная и удобная библиотека, которая упрощает имитацию HTTP-запросов и ответов.
Улучшение стратегии тестирования HTTP-кода
• Подход предполагает изолирование HTTP-кода в отдельные компоненты, что позволяет проводить независимое тестирование остальной системы.
• Несмотря на то, что данный подход упрощает репозитории и тестовые классы, он часто пренебрегает тестированием самого HTTP-кода из-за сопутствующих сложностей.
• В результате возникает потребность проверять важнейшую логику, связанную с HTTP-запросами и парсингом JSON.
Данная проблема прослеживается в проектах, задействующих Retrofit.
Эта библиотека абстрагирует HTTP-логику посредством интерфейсов. Рассмотрим следующий фрагмент кода:
interface GitHubService {
@GET("users/{user}/repos")
suspend fun listRepos(@Path("user") user: String): List<RepoDto>
}
class GitHubRepositoryImpl(
private val githubService: GitHubService,
private val repoMapper: RepoMapper
): GitHubRepository {
override suspend fun listRepos(user: String): List<Repo> {
val repoDtos = githubService.listRepos(user)
return repoMapper.map(repoDtos)
}
}
Хотя этот подход упрощает репозитории и тестовые классы, он часто пренебрегает важными тестами для HTTP-логики. Кроме того, существует вероятность создания ненужных компонентов исключительно для упрощения модульного тестирования репозиториев.Цель модульного тестирования — предотвращать и выявлять ошибки разработчиков, тем самым гарантируя правильную работу кода. Без надлежащих тестов даже простые ошибки, такие как изменение пути с
users/{user}/repos на user/{user}/repos
или переименование поля в RepoDto, могут остаться незамеченными, что неприемлемо в профессиональном проекте. Я вовсе не критикую Retrofit. Наоборот, считаю ее отличной и удобочитаемой библиотекой.
Более того, признаю целесообразность изолирования HTTP-кода в отдельные компоненты ради улучшения структуры и удобства сопровождения.
Я просто указываю на недостатки стратегии тестирования, которую наблюдал во многих проектах. Она вынуждает больше внимания уделять комплексному тестированию HTTP-кода, вследствие чего потенциальные проблемы остаются незамеченными. Акцентируя данную проблему, призываю разработчиков с особой ответственностью подходить к тестированию HTTP-логики, тем самым обеспечивая общее качество и надежность проектов.
Имитация запросов и ответов с помощью NetMock
@Test
fun `your test`() = runTest {
val user = "some_user"
netMock.addMock(
request = {
method = Method.Get
requestUrl = "https://api.github.com/users/$user/repos"
},
response = {
code = 200
body = readFromResources("responses/repo_list.json")
}
)
val result = sut.listRepos(user)
//...
}
Для имитации запросов и ответов применяется простой API, предоставляемый NetMock.
Добавляя mock-объект в очередь ожидаемых запросов и ответов, вы можете перехватывать и контролировать поведение HTTP-взаимодействий во время тестирования.
Создание экземпляра NetMock
Читать
@javatg
Please open Telegram to view this post
VIEW IN TELEGRAM
🎉🎉🎉 Вышел Kotlin 1.9.10
Исправлено много багов, среди них, самые интересные:
В компиляторе:
KT-60659 unresolved reference: addFirst в JDK 21
KT-60986 Статические кеши в рантайме на Linux
KT-60231 NoClassDefFoundError: $$$$$NON_LOCAL_RETURN$$$$$ после обновы до 1.9.0
Gradle:
KT-61147 Компилятор 1.9.0 и Gson
KT-60543 NoClassDefFoundError для com/gradle/scan/plugin/BuildScanExtension
Native:
KT-60230 "unknown options: -ios_simulator_version_min -sdk_version" на Xcode 15 beta 3
KT-60758 'iOS-simulator' в dylib сборке для 'iOS' в Xcode 15 beta 4
https://fossies.org/linux/misc/kotlin-1.9.10.tar.gz/
@javatg
Исправлено много багов, среди них, самые интересные:
В компиляторе:
KT-60659 unresolved reference: addFirst в JDK 21
KT-60986 Статические кеши в рантайме на Linux
KT-60231 NoClassDefFoundError: $$$$$NON_LOCAL_RETURN$$$$$ после обновы до 1.9.0
Gradle:
KT-61147 Компилятор 1.9.0 и Gson
KT-60543 NoClassDefFoundError для com/gradle/scan/plugin/BuildScanExtension
Native:
KT-60230 "unknown options: -ios_simulator_version_min -sdk_version" на Xcode 15 beta 3
KT-60758 'iOS-simulator' в dylib сборке для 'iOS' в Xcode 15 beta 4
https://fossies.org/linux/misc/kotlin-1.9.10.tar.gz/
@javatg
⏱ Все что вам нужно знать о таймаутах
Зачем устанавливать таймауты
Давайте для начала ответим на простой вопрос: "Зачем устанавливать таймауты?". Успешный ответ сервиса, даже если он занимает много времени, лучше, чем ошибка закрытия соединения по таймауту.
▪️ Хм... не всегда, давайте разбираться.
Прежде всего если сервис не отвечает или отвечает слишком медленно, никто не будет ждать. Вместо того чтобы испытывать терпение ваших пользователей, следуйте принципу fail-fast. Позвольте вашим клиентам повторить запрос или обработать ошибку на их стороне. Когда возможно, возвращайте fallback значение.
Другой важный аспект - это утилизация ресурсов. В то время пока клиент ожидает ответ, различные ресурсы используются на стороне сервиса: треды, http коннекшены, коннекшены базы данных и т.д. Даже если клиент закроет соединение, без правильной конфигурации таймаутов запрос будет обрабатываться на сервере, а это значит что ресурсы будут заняты.
Использование бесконечных или слишком больших таймаутов - это плохая стратегия. Некоторое время вы не будете замечать проблему, до тех пор пока у одного из сервисов, который вы используете, не начнутся проблемы и у вас исчерпается конекшен-пул.
К сожалению, многие библиотеки устанавливают слишком большой таймаут по дефолту, или отключают таймауты совсем. Авторы библиотек стремятся привлечь как можно больше пользователей и стараются сделать так, чтобы их библиотека работала в большинстве ситуаций.
Но для продакшен сервисов - это недопустимо. Это может быть даже опасным. Например, в нативном java HttClient connection/request таймауты отключены, я не думаю что это в рамках вашего SLA :)
📌 Продолжение
@javatg
Зачем устанавливать таймауты
Давайте для начала ответим на простой вопрос: "Зачем устанавливать таймауты?". Успешный ответ сервиса, даже если он занимает много времени, лучше, чем ошибка закрытия соединения по таймауту.
Прежде всего если сервис не отвечает или отвечает слишком медленно, никто не будет ждать. Вместо того чтобы испытывать терпение ваших пользователей, следуйте принципу fail-fast. Позвольте вашим клиентам повторить запрос или обработать ошибку на их стороне. Когда возможно, возвращайте fallback значение.
Другой важный аспект - это утилизация ресурсов. В то время пока клиент ожидает ответ, различные ресурсы используются на стороне сервиса: треды, http коннекшены, коннекшены базы данных и т.д. Даже если клиент закроет соединение, без правильной конфигурации таймаутов запрос будет обрабатываться на сервере, а это значит что ресурсы будут заняты.
Использование бесконечных или слишком больших таймаутов - это плохая стратегия. Некоторое время вы не будете замечать проблему, до тех пор пока у одного из сервисов, который вы используете, не начнутся проблемы и у вас исчерпается конекшен-пул.
К сожалению, многие библиотеки устанавливают слишком большой таймаут по дефолту, или отключают таймауты совсем. Авторы библиотек стремятся привлечь как можно больше пользователей и стараются сделать так, чтобы их библиотека работала в большинстве ситуаций.
Но для продакшен сервисов - это недопустимо. Это может быть даже опасным. Например, в нативном java HttClient connection/request таймауты отключены, я не думаю что это в рамках вашего SLA :)
@javatg
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔋Основы реактивного программирования
Reactor
Reactor — это библиотека реактивного программирования для создания асинхронных и событийно-управляемых приложений на языке Java. Она предназначена для масштабируемой и эффективной обработки больших объемов потоков данных и событий. Я сам пользовался Reactor, и поэтому решил предложить вам эту простую и доступную для новичков библиотеку в демонстрационных целях.
Основное назначение Reactor — предоставление набора инструментов и абстракций для создания реактивных приложений. Reactor предлагает набор основных компонентов, таких как Flux и Mono, которые представляют собой потоки данных и событий. Эти компоненты могут быть объединены и преобразованы с помощью набора операторов, таких как map, filter и reduce, для создания сложных потоков данных.
Сравнение Mono и Flux
Как уже говорилось, реактивное программирование работает по модели “издатель-подписчик”. И Mono, и Flux предоставляют абстракцию для публикации событий.
Разница между ними заключается в том, что Mono может публиковать только одно событие за раз, а Flux — несколько событий за раз. Поэтому когда нужно публиковать только одно событие, выбирают Mono, а когда нужно публиковать несколько событий — Flux.
Теперь приведу небольшой пример, поясняющий каждую упоминаемую ранее концепцию.
Демо
Все начинается с того, что издатель выдает событие для потребителя.
Как и java-потоки, Flux и Mono придерживаются ленивого выполнения, т.е. значение выражения не обрабатывается до тех пор, пока в нем нет необходимости.
Начнем с создания простого Flux.
Метод “just” позволяет создать Flux, если есть доступное значение. Это самый простой способ создания Flux. Несмотря на то что я привожу пример с Flux, шаги остаются такими же и для Mono. В Mono также используется метод “just” с той разницей, что Mono применяется только для одного элемента. Поэтому если задать несколько значений, то получим ошибку.
Здесь был использован оператор для обработки данных. Как вы помните, я уже упоминал об операторах. Теперь хотел бы перечислить несколько распространенных операторов.
1. Map: преобразует каждый элемент, выдаваемый потоком данных, применяя к нему функцию.
2. Filter: фильтрует элементы, выдаваемые потоком данных, на основе предикатной функции.
3. Reduce: агрегирует элементы, выдаваемые потоком данных, в одно значение с помощью функции-аккумулятора.
4. Merge: объединяет несколько потоков данных в один.
5. Concat: конкатенирует нескольких потоков данных в один в определенном порядке.
6. FlatMap: преобразует каждый элемент, выдаваемый потоком данных, в другой поток данных, а затем сводит полученные потоки в один.
7. Zip: объединяет элементы, выдаваемые несколькими потоками данных, в один элемент с помощью функции-комбинатора.
Применим еще несколько операторов для пояснения их работы.
Flux преобразуется в:
Теперь сведем Flux к одной строке.
Flux преобразуется в:
Эти шаги представляют собой обработку события. Мы начали с исходного события Flux, состоящего из списка чисел, и использовали операторы для обработки событий.
*️⃣ Следует отметить, что я добавил преобразованный вывод для пояснения, а на самом деле никаких вычислений не происходит. Как уже говорилось, Flux следует модели ленивого исполнения. Пока мы не подпишемся на поток событий, никаких вычислений не происходит. А когда подписываемся, получаем возможность контролировать то, что нужно сделать с полученным событием.
Чтобы упростить задачу и показать, как работает подписчик, просто выведу Flux.
📌Читать
@javatg
Reactor
Reactor — это библиотека реактивного программирования для создания асинхронных и событийно-управляемых приложений на языке Java. Она предназначена для масштабируемой и эффективной обработки больших объемов потоков данных и событий. Я сам пользовался Reactor, и поэтому решил предложить вам эту простую и доступную для новичков библиотеку в демонстрационных целях.
Основное назначение Reactor — предоставление набора инструментов и абстракций для создания реактивных приложений. Reactor предлагает набор основных компонентов, таких как Flux и Mono, которые представляют собой потоки данных и событий. Эти компоненты могут быть объединены и преобразованы с помощью набора операторов, таких как map, filter и reduce, для создания сложных потоков данных.
Сравнение Mono и Flux
Как уже говорилось, реактивное программирование работает по модели “издатель-подписчик”. И Mono, и Flux предоставляют абстракцию для публикации событий.
Разница между ними заключается в том, что Mono может публиковать только одно событие за раз, а Flux — несколько событий за раз. Поэтому когда нужно публиковать только одно событие, выбирают Mono, а когда нужно публиковать несколько событий — Flux.
Теперь приведу небольшой пример, поясняющий каждую упоминаемую ранее концепцию.
Демо
Все начинается с того, что издатель выдает событие для потребителя.
Как и java-потоки, Flux и Mono придерживаются ленивого выполнения, т.е. значение выражения не обрабатывается до тех пор, пока в нем нет необходимости.
Начнем с создания простого Flux.
Flux<Integer> flux = Flux.just(1, 2, 3, 4, 5);
Метод “just” позволяет создать Flux, если есть доступное значение. Это самый простой способ создания Flux. Несмотря на то что я привожу пример с Flux, шаги остаются такими же и для Mono. В Mono также используется метод “just” с той разницей, что Mono применяется только для одного элемента. Поэтому если задать несколько значений, то получим ошибку.
Flux<String> mappedFlux = flux.map(i -> "Number: " + i)
The above flux gets converted to
"Number: 1", "Number: 2", "Number: 3", "Number: 4", "Number: 5"
Здесь был использован оператор для обработки данных. Как вы помните, я уже упоминал об операторах. Теперь хотел бы перечислить несколько распространенных операторов.
1. Map: преобразует каждый элемент, выдаваемый потоком данных, применяя к нему функцию.
2. Filter: фильтрует элементы, выдаваемые потоком данных, на основе предикатной функции.
3. Reduce: агрегирует элементы, выдаваемые потоком данных, в одно значение с помощью функции-аккумулятора.
4. Merge: объединяет несколько потоков данных в один.
5. Concat: конкатенирует нескольких потоков данных в один в определенном порядке.
6. FlatMap: преобразует каждый элемент, выдаваемый потоком данных, в другой поток данных, а затем сводит полученные потоки в один.
7. Zip: объединяет элементы, выдаваемые несколькими потоками данных, в один элемент с помощью функции-комбинатора.
Применим еще несколько операторов для пояснения их работы.
Flux<String> filteredFlux = mappedFlux.filter(s -> !s.endsWith("2") && !s.endsWith("4"));
Flux преобразуется в:
"Number: 1", "Number: 3", "Number: 5"
Теперь сведем Flux к одной строке.
Mono<String> reducedMono = filteredFlux.reduce("", (a, b) -> a+ ", " + b);
Flux преобразуется в:
"Number: 1, Number: 3, Number: 5"
Эти шаги представляют собой обработку события. Мы начали с исходного события Flux, состоящего из списка чисел, и использовали операторы для обработки событий.
Чтобы упростить задачу и показать, как работает подписчик, просто выведу Flux.
📌Читать
@javatg
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥 Дайджест полезных материалов из мира : Java за неделю
Почитать:
— Все о нестатических блоках инициализации в Java
— Jackson ObjectMapper Streaming API без возни
— Релиз Jmix 2.0 — новой версии платформы для быстрой разработки бизнес-приложений на Java
— Компилирование «железного» бинарника Java-программы Google Closure Stylesheets с GraalVM
— Нагрузочное тестирование API без использования UI
— Удобная подсветка покрытия кода тестами в Merge Request GitLab
— Генерируем простой web интерфейс для просмотра таблиц PostgreSQL
— Реквием по «Расскажи, как работает HashMap?»
— Разбираемся в «базовых» алгоритмах для проекта
— Почему WebAssembly плохо годится для Java
— Why Java Is an Object-Oriented Programming Language?
— Leet Code — 2744. Find Maximum Number of String Pairs
— Data Science with Python and Java: A Dynamic Duo for Modern Analytics
— Curso de Programação para Iniciantes Intensivo e Gratuito
— Leet Code — 709. To Lower Case
— What Happens When We Run a .java Code?
— Captura de dados antes de Captura de linha: "erro" e solução, em Java.
— Javascript ➡️ Java: Strings
— Testing Native Queries with Functions in Spring Boot with JPA and H2
Посмотреть:
🌐 Code Reflection (⏱ 41:46)
🌐 Upgrading from Java 17 to 21 #RoadTo21 (⏱ 24:41)
🌐 Continuations - Under the Covers (⏱ 34:07)
🌐 Java 21 new feature: Virtual Threads #RoadTo21 (⏱ 33:35)
🌐 Difference between and intermediate and a terminal operation? - Cracking the Java Coding Interview (⏱ 01:00)
🌐 What list can you pass to a method that takes a List of Number? - Cracking the Java Coding Interview (⏱ 00:54)
🌐 Generational ZGC and Beyond (⏱ 33:34)
🌐 Project Lilliput - Compressed Object Headers (⏱ 51:26)
🌐 Fast JVM Startup with Checkpoint & Restore (⏱ 47:28)
🌐 Java 21 JVM & GC Improvements #RoadTo21 (⏱ 12:09)
Хорошего дня!
@javatg
Почитать:
— Все о нестатических блоках инициализации в Java
— Jackson ObjectMapper Streaming API без возни
— Релиз Jmix 2.0 — новой версии платформы для быстрой разработки бизнес-приложений на Java
— Компилирование «железного» бинарника Java-программы Google Closure Stylesheets с GraalVM
— Нагрузочное тестирование API без использования UI
— Удобная подсветка покрытия кода тестами в Merge Request GitLab
— Генерируем простой web интерфейс для просмотра таблиц PostgreSQL
— Реквием по «Расскажи, как работает HashMap?»
— Разбираемся в «базовых» алгоритмах для проекта
— Почему WebAssembly плохо годится для Java
— Why Java Is an Object-Oriented Programming Language?
— Leet Code — 2744. Find Maximum Number of String Pairs
— Data Science with Python and Java: A Dynamic Duo for Modern Analytics
— Curso de Programação para Iniciantes Intensivo e Gratuito
— Leet Code — 709. To Lower Case
— What Happens When We Run a .java Code?
— Captura de dados antes de Captura de linha: "erro" e solução, em Java.
— Javascript ➡️ Java: Strings
— Testing Native Queries with Functions in Spring Boot with JPA and H2
Посмотреть:
🌐 Code Reflection (⏱ 41:46)
🌐 Upgrading from Java 17 to 21 #RoadTo21 (⏱ 24:41)
🌐 Continuations - Under the Covers (⏱ 34:07)
🌐 Java 21 new feature: Virtual Threads #RoadTo21 (⏱ 33:35)
🌐 Difference between and intermediate and a terminal operation? - Cracking the Java Coding Interview (⏱ 01:00)
🌐 What list can you pass to a method that takes a List of Number? - Cracking the Java Coding Interview (⏱ 00:54)
🌐 Generational ZGC and Beyond (⏱ 33:34)
🌐 Project Lilliput - Compressed Object Headers (⏱ 51:26)
🌐 Fast JVM Startup with Checkpoint & Restore (⏱ 47:28)
🌐 Java 21 JVM & GC Improvements #RoadTo21 (⏱ 12:09)
Хорошего дня!
@javatg
🚀 Как интегрировать Kafka со Spring Boot
Платформа распределенной потоковой передачи Kafka предоставляет надежную и отказоустойчивую систему обмена сообщениями, позволяющую обрабатывать данные в режиме реального времени.
Для создания эффективных, несвязанных и отзывчивых приложений разработчики могут гармонично реализовать возможности Kafka в сочетании с простотой и производительностью фреймворка Spring Boot.
Рассмотрим пошаговую интеграцию Kafka и Spring Boot.
Шаг 1. Настройте Kafka
Установите Kafka и запустите кластер Kafka. Инструкции по установке есть в официальной документации Kafka.
Шаг 2. Создайте проект Spring Boot
Настройте новый проект Spring Boot, используя предпочитаемую IDE или Spring Initializr. Включите необходимые зависимости:
spring-boot-starter-web включает веб-функции в Spring Boot.
Шаг 3. Настройте свойства Kafka
В файле проекта Spring Boot application.properties настройте необходимые свойства Kafka, такие как серверы начальной загрузки и названия разделов, а также любые дополнительные. Например:
Шаг 4. Создайте Kafka Producer
Внедрите Kafka producer для отправки сообщений в разделы (topic) Kafka. Создать простой producer позволяет KafkaTemplate, предоставленный Spring Kafka. Например:
📌Читать
@javatg
Платформа распределенной потоковой передачи Kafka предоставляет надежную и отказоустойчивую систему обмена сообщениями, позволяющую обрабатывать данные в режиме реального времени.
Для создания эффективных, несвязанных и отзывчивых приложений разработчики могут гармонично реализовать возможности Kafka в сочетании с простотой и производительностью фреймворка Spring Boot.
Рассмотрим пошаговую интеграцию Kafka и Spring Boot.
Шаг 1. Настройте Kafka
Установите Kafka и запустите кластер Kafka. Инструкции по установке есть в официальной документации Kafka.
Шаг 2. Создайте проект Spring Boot
Настройте новый проект Spring Boot, используя предпочитаемую IDE или Spring Initializr. Включите необходимые зависимости:
spring-kafka
предоставляет основную функциональность для интеграции Kafka в Spring;spring-boot-starter-web включает веб-функции в Spring Boot.
Шаг 3. Настройте свойства Kafka
В файле проекта Spring Boot application.properties настройте необходимые свойства Kafka, такие как серверы начальной загрузки и названия разделов, а также любые дополнительные. Например:
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=my-group
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.template.default-topic=my-topic
Шаг 4. Создайте Kafka Producer
Внедрите Kafka producer для отправки сообщений в разделы (topic) Kafka. Создать простой producer позволяет KafkaTemplate, предоставленный Spring Kafka. Например:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
@Service
public class KafkaProducerService {
private final KafkaTemplate<String, String> kafkaTemplate;
@Autowired
public KafkaProducerService(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void sendMessage(String message) {
kafkaTemplate.send("my-topic", message);
}
}
📌Читать
@javatg
Spring Modulith 1.0 GA Released. Новый -релиз получила Spring Modulith — часть экосистемы Spring, призванная упростить построение модульных монолитов.
Announcing Testcontainers Desktop Free Application — свежий анонс утилиты, удобной для работы с тест-контейнерами. Из интересного — быстрое переключение между docker/colima/podman, возможность запуска контейнеров на фиксированных портах и их фриз на время дебага.
Выпущен Jmix 2.0 — новая версия платформы для быстрой разработки бизнес-приложений на Java. В статье — подробности о том, для чего нужен этот инструмент и как он позволяет сокращать время разработки бизнес-приложения.
OpenAI выложила на Github OpenCopilot. OpenCopilot — это ИИ-помощник, способный интегрироваться в продукт. Он готов к встраиванию через базовые API и может вызывать эти API по необходимости. Модель OpenCopilot основана на обширной лингвистической базе и способна определить, требует ли запрос пользователя выполнения вызова к какой-либо конечной точке API.
Дмитрий Иванов, Андрей Кулешов — Пирамида потребностей Маслоу для Java/Kotlin-разработчика. Виде — это полноценная методичка, посвященная тому, как начать и развить свой open-source-проект. От стадии кодинга и до построения комьюнити — каждый шаг расписан детально, с приведением полезных инструментов и практик.
JDK 21 G1/Parallel/Serial GC changes — статья о том, как изменятся сборщики мусора в JDK 21, и о планах их развития. Основные изменения коснутся G1 GС:
@javatg
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥 Дайджест полезных материалов из мира : Java за неделю
Почитать:
— Всё, что вы хотели знать о Java, но не доходили руки спросить: что будет на Joker 2023
— Как добавить кастомный аутентификатор в KeyCloak и подружить его со сторонней системой
— Java Digest #4
— Высокие технологии или дешевые фокусы с двойным дном
— Байки джависта
— Создание приложения для распознавания текста с изображений и аудиофайлов
— Параллельность в Java на практике
— Динамическое создание слушателей в Kafka
— Аудит пользователей Spring Data JPA
— Ускоряем java-рефлексию в 2023
— 🔐 Unlock the Power of Data Security with Our Online Encryption and Decryption Tool! 🔐
— Securing the Path: A Comprehensive Guide to Spring Security Migration
— Elevate Your Security Game: Spring Security’s Lambda DSL Unleashed
— Parâmetros em API RESTful: Tipos e Exemplos
— Kafka in a Nutshell 🌰: Events, Topics, and APIs made simple
— Testes Mutantes com Pitest
— Aumente a Qualidade do seu Código Java: Um Guia para Utilizar o JaCoCo em sua API REST
— Simplifying Java Multithreading (Runnable interface) with a Construction Analogy
— Application and Webserver Logging in Spring Boot 3.1
— Exercising DSA with Java
Посмотреть:
🌐 Java 21 JVM & GC Improvements #RoadTo21 (⏱ 12:09)
🌐 Value Objects in Valhalla (⏱ 51:42)
🌐 Learn All You Need To Know For Your Java 17-21 Update #Java #Java21 #RoadTo21 (⏱ 00:46)
🌐 Java 21 Tool Enhancements: Better Across the Board #RoadTo21 (⏱ 17:39)
🌐 What is a deadlock? - Cracking the Java Coding Interview (⏱ 00:56)
🌐 Project Leyden - Capturing Lightning in a Bottle (⏱ 47:57)
🌐 Creating an instance of a Class using the Reflection API? - Cracking the Java Coding Interview (⏱ 00:53)
🌐 Everything You Never Wanted to Know about Java Class Initialization (⏱ 39:01)
Хорошего дня!
@javatg
Почитать:
— Всё, что вы хотели знать о Java, но не доходили руки спросить: что будет на Joker 2023
— Как добавить кастомный аутентификатор в KeyCloak и подружить его со сторонней системой
— Java Digest #4
— Высокие технологии или дешевые фокусы с двойным дном
— Байки джависта
— Создание приложения для распознавания текста с изображений и аудиофайлов
— Параллельность в Java на практике
— Динамическое создание слушателей в Kafka
— Аудит пользователей Spring Data JPA
— Ускоряем java-рефлексию в 2023
— 🔐 Unlock the Power of Data Security with Our Online Encryption and Decryption Tool! 🔐
— Securing the Path: A Comprehensive Guide to Spring Security Migration
— Elevate Your Security Game: Spring Security’s Lambda DSL Unleashed
— Parâmetros em API RESTful: Tipos e Exemplos
— Kafka in a Nutshell 🌰: Events, Topics, and APIs made simple
— Testes Mutantes com Pitest
— Aumente a Qualidade do seu Código Java: Um Guia para Utilizar o JaCoCo em sua API REST
— Simplifying Java Multithreading (Runnable interface) with a Construction Analogy
— Application and Webserver Logging in Spring Boot 3.1
— Exercising DSA with Java
Посмотреть:
🌐 Java 21 JVM & GC Improvements #RoadTo21 (⏱ 12:09)
🌐 Value Objects in Valhalla (⏱ 51:42)
🌐 Learn All You Need To Know For Your Java 17-21 Update #Java #Java21 #RoadTo21 (⏱ 00:46)
🌐 Java 21 Tool Enhancements: Better Across the Board #RoadTo21 (⏱ 17:39)
🌐 What is a deadlock? - Cracking the Java Coding Interview (⏱ 00:56)
🌐 Project Leyden - Capturing Lightning in a Bottle (⏱ 47:57)
🌐 Creating an instance of a Class using the Reflection API? - Cracking the Java Coding Interview (⏱ 00:53)
🌐 Everything You Never Wanted to Know about Java Class Initialization (⏱ 39:01)
Хорошего дня!
@javatg
Многие, возможно, думают, что работа с байт-кодом Java (будь то чтение или, тем более, генерация) — это какая-то особенная магия, доступная только продвинутым разработчикам с особенно крутым опытом. На самом деле, я считаю такую точку зрения ошибочной.
JVM устроена гораздо проще, чем CPU; она оперирует такими высокоуровневыми понятиями как классы, интерфейсы, методы, а не просто лопатит байты в памяти. В отличие от CPU, который легко уронить криво сгенерированным машинным кодом, JVM заботливо отверифицирует любой байт-код и в общем не даст выстрелить в ногу.
Но с чего начать погружение в байт-кодную магию? В сети есть некоторое количество туториалов по этому вопросу. Как мне кажется, они либо показывают слишком простые случаи, от которых непонятно, как перейти к чему-то более интересному, либо очень основательные и требуют вникать в теорию, собирать целиком картину в голове по кусочкам. Я хотел бы попробовать внести свой вклад в эту тему — надеюсь, у меня получится показать, как можно побороть первый страх и написать что-то похожее на реалистичный сценарий без особого вникания в теорию на первом этапе.
Весь приведённый код доступен в репозитории.
Задача
Я вдохновился книгой Бьёрна Страуструпа, по которой лет 20 назад изучал C++. В одной из первых глав в качестве задачи для введения в язык предлагается написать калькулятор выражений. Я же предлагаю не вычислять выражения, а генерировать байт-код, который вычисляет выражения.
Итак, формулировка: необходимо написать метод, которые принимает на вход строку с математическими выражениями и выдаёт на выходе экземпляр такого интерфейса:
public interface Expression {
double evaluate(Function<String, Double> inputs);
}
Выражения в списке разделены точкой с запятой (;), метод evaluate возвращает результат вычисления последнего из выражений. Выражения определим так:
Число (например, 2, 42, 3.14) — это выражение.
Идентификатор (например, foo, pi, myVar_1) — это выражение. Значение по-умолчанию для переменной вычисляется с помощью вызова inputs.apply(id).
Если A и B — выражения, то A + B, A - B, A * B, A / B, -A, (A) — так же выражения
Если A — это идентификатор, и B — это выражение, то A = B — так же выражение
Генерируем класс
Для начала напишем генератор класса, реализующего интерфейс Expression.
▪Читать дальше
▪Github
@javatg
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡Легкий способ получать свежие обновлении и следить за трендами в разработке на вашем языке. Находите свой стек и подписывайтесь:
Data Science: www.tg-me.com/data_analysis_ml
Java: www.tg-me.com/java_library
Базы данных: www.tg-me.com/sqlhub
Машинное обучение: www.tg-me.com/ai_machinelearning_big_data
Go: www.tg-me.com/Golang_google
C/C++/ www.tg-me.com/cpluspluc
C#: www.tg-me.com/csharp_ci
Хакинг: www.tg-me.com/linuxkalii
Мобильная разработка: www.tg-me.com/mobdevelop
Docker: www.tg-me.com/+0WdB4uvOwCY0Mjdi
Python: www.tg-me.com/python_job_interview
Rust: www.tg-me.com/rust_code
Javascript: www.tg-me.com/javascriptv
React: www.tg-me.com/react_tg
PHP: www.tg-me.com/phpshka
Android: www.tg-me.com/android_its
Linux: www.tg-me.com/+A8jY79rcyKJlYWY6
Big Data: www.tg-me.com/bigdatai
Devops: www.tg-me.com/devOPSitsec
Тестирование:https://www.tg-me.com/+F9jPLmMFqq1kNTMy
Собеседования: https://www.tg-me.com/machinelearning_interview
💼 Папка с вакансиями: www.tg-me.com/addlist/_zyy_jQ_QUsyM2Vi
Папка Go разработчика: www.tg-me.com/addlist/MUtJEeJSxeY2YTFi
Папка Python разработчика: www.tg-me.com/addlist/eEPya-HF6mkxMGIy
Папка ML: https://www.tg-me.com/addlist/2Ls-snqEeytkMDgy
😆ИТ-Мемы: www.tg-me.com/memes_prog
🇬🇧Английский: www.tg-me.com/english_forprogrammers
ИИ: www.tg-me.com/vistehno
📕Ит-книги бесплатно: https://www.tg-me.com/addlist/BkskQciUW_FhNjEy
Data Science: www.tg-me.com/data_analysis_ml
Java: www.tg-me.com/java_library
Базы данных: www.tg-me.com/sqlhub
Машинное обучение: www.tg-me.com/ai_machinelearning_big_data
Go: www.tg-me.com/Golang_google
C/C++/ www.tg-me.com/cpluspluc
C#: www.tg-me.com/csharp_ci
Хакинг: www.tg-me.com/linuxkalii
Мобильная разработка: www.tg-me.com/mobdevelop
Docker: www.tg-me.com/+0WdB4uvOwCY0Mjdi
Python: www.tg-me.com/python_job_interview
Rust: www.tg-me.com/rust_code
Javascript: www.tg-me.com/javascriptv
React: www.tg-me.com/react_tg
PHP: www.tg-me.com/phpshka
Android: www.tg-me.com/android_its
Linux: www.tg-me.com/+A8jY79rcyKJlYWY6
Big Data: www.tg-me.com/bigdatai
Devops: www.tg-me.com/devOPSitsec
Тестирование:https://www.tg-me.com/+F9jPLmMFqq1kNTMy
Собеседования: https://www.tg-me.com/machinelearning_interview
💼 Папка с вакансиями: www.tg-me.com/addlist/_zyy_jQ_QUsyM2Vi
Папка Go разработчика: www.tg-me.com/addlist/MUtJEeJSxeY2YTFi
Папка Python разработчика: www.tg-me.com/addlist/eEPya-HF6mkxMGIy
Папка ML: https://www.tg-me.com/addlist/2Ls-snqEeytkMDgy
😆ИТ-Мемы: www.tg-me.com/memes_prog
🇬🇧Английский: www.tg-me.com/english_forprogrammers
ИИ: www.tg-me.com/vistehno
📕Ит-книги бесплатно: https://www.tg-me.com/addlist/BkskQciUW_FhNjEy