📌 Что такое
Ключевое слово
✔️ Основные особенности:
1️⃣ Обеспечивает чтение переменной из основной памяти, а не из кэша потока.
2️⃣ Гарантирует видимость изменений между потоками.
Пример:
💡 Ограничение:
#java #volatile #multithreading
volatile и как оно работает?Ключевое слово
volatile в Java используется для переменных, к которым обращаются несколько потоков. Оно гарантирует, что изменения переменной одним потоком будут видны другим.1️⃣ Обеспечивает чтение переменной из основной памяти, а не из кэша потока.
2️⃣ Гарантирует видимость изменений между потоками.
Пример:
class SharedObject {
volatile int count = 0;
}volatile не гарантирует атомарность операций. Для этого используйте synchronized или классы из java.util.concurrent.#java #volatile #multithreading
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2
Метод
deepToString из класса Arrays используется для создания строкового представления многомерных массивов (например, массивов массивов). Он обходит каждый уровень вложенности массива и выводит его элементы в виде строки. Это удобно для работы с многомерными массивами, так как стандартный метод toString не раскрывает их структуру.Пример использования:
int[][] array = {{1, 2, 3}, {4, 5, 6}};
System.out.println(Arrays.deepToString(array));
// [[1, 2, 3], [4, 5, 6]]
Этот метод работает рекурсивно, обеспечивая полное отображение структуры массива любой вложенности.
#java #Arrays #deepToString
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤2
StringBuilder и StringBufferИ
StringBuilder, и StringBuffer используются для работы со строками, но между ними есть важные отличия.✔️ StringBuilder быстрее, но не потокобезопасен.
✔️ StringBuffer потокобезопасен, но медленнее из-за синхронизации.
StringBuilder:StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb); // Hello WorldStringBuffer:StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");
System.out.println(sb); // Hello WorldStringBuilder для лучшей производительности.#java #stringbuilder #stringbuffer
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍1🔥1🆒1
✔️ В Java класс может одновременно реализовать несколько интерфейсов, но наследоваться только от одного класса.
✔️ Абстрактные классы используются только тогда, когда присутствует тип отношений «is a» (является). Интерфейсы могут реализоваться классами, которые не связаны друг с другом.
✔️ Абстрактный класс - средство, позволяющее избежать написания повторяющегося кода, инструмент для частичной реализации поведения. Интерфейс - это средство выражения семантики класса, контракт, описывающий возможности. Все методы интерфейса неявно объявляются как
public abstract или (начиная с Java 8) default - методами с реализацией по-умолчанию, а поля - public static final.✔️ Интерфейсы позволяют создавать структуры типов без иерархии.
✔️ Наследуясь от абстрактного, класс «растворяет» собственную индивидуальность. Реализуя интерфейс, он расширяет собственную функциональность.
Абстрактные классы содержат частичную реализацию, которая дополняется или расширяется в подклассах. При этом все подклассы схожи между собой в части реализации, унаследованной от абстрактного класса, и отличаются лишь в части собственной реализации абстрактных методов родителя. Поэтому абстрактные классы применяются в случае построения иерархии однотипных, очень похожих друг на друга классов. В этом случае наследование от абстрактного класса, реализующего поведение объекта по умолчанию может быть полезно, так как позволяет избежать написания повторяющегося кода. Во всех остальных случаях лучше использовать интерфейсы.
#java #interface #abstract
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1🥴1
Comparator и ComparableВ
Java для сортировки объектов используются два ключевых интерфейса: Comparable и Comparator. Понимание их отличий и применения поможет вам эффективно управлять сортировкой данных в коллекциях.📚 Краткие определения:
- Comparable:
- Используется, когда класс должен иметь естественный порядок. Например, мы можем сортировать людей по возрасту.
- Сигнатура метода:
int compareTo(T o).Пример:
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Person other) {
return Integer.compare(this.age, other.age); // Сравнение по возрасту
}
}
- Comparator:
- Используется для создания внешних стратегий сравнения, позволяя определять несколько способов сортировки. Подходит для сортировки объектов разных классов.
- Сигнатура метода:
int compare(T o1, T o2).Пример:
import java.util.Comparator;
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name); // Сравнение по имени
}
}
⚖️
Когда использовать:- Comparable:
- Используйте, если у класса есть естественный порядок. Например, чтобы сортировать людей по возрасту.
- Сортировка с помощью
Collections.sort() или Arrays.sort() будет проще.Пример использования:
List<Person> people = Arrays.asList(new Person("Alice", 30), new Person("Bob", 25));
Collections.sort(people); // Сортировка по возрасту с использованием Comparable
- Comparator:
- Идеален для ситуаций, когда необходимо изменить порядок сортировки или сравнивать объекты разных классов.
Пример использования:
List<Person> people = Arrays.asList(new Person("Alice", 30), new Person("Bob", 25));
Collections.sort(people, new NameComparator()); // Сортировка по имени с использованием Comparator
#java #Comparator #Comparable
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2❤1
ThreadLocal выглядит как магия:У каждого потока будет своё собственное значениеИ это правда. Но именно из-за этого он часто становится источником очень неприятных багов в production.
📦 Как работает ThreadLocal
ThreadLocal<String> currentUser = new ThreadLocal<>();
currentUser.set("Alex");
Теперь в каждом потоке хранится своё значение:
System.out.println(currentUser.get());
🔥 Где это используют
* Spring Security
* Hibernate
* логирование (`traceId`)
* транзакции
* хранение request context
🤔 Кажется идеально. В чём проблема?
Проблема начинается с thread pool.
⚠️ Главная ловушка
ThreadLocal<String> local = new ThreadLocal<>();
executor.submit(() -> {
local.set("DATA");
});
Поток из пула НЕ уничтожается после задачи.
👉 Значение остаётся висеть внутри потока.
💣 Что это вызывает
* утечки памяти
* неожиданные данные между запросами
* "рандомные" баги в серверных приложениях
😨 Самое неприятное
Даже если сам
ThreadLocal удалён GC,значение внутри потока может продолжать жить.
Почему?
Потому что внутри
Thread:* ключ (`ThreadLocal`) хранится как weak reference
* а значение - как strong reference
👉 Ключ умер
👉 Значение осталось
Классическая hidden memory leak.
✅ Правильный подход
ВСЕГДА чистить
ThreadLocal:
try {
local.set("DATA");
// работа
} finally {
local.remove();
}
🚀 Почему это критично в Spring / Tomcat
Сервер использует thread pool:
* один поток обслуживает тысячи запросов
* забытый
ThreadLocal живёт очень долгоОтсюда:
* рост памяти
* странное поведение пользователей
* “невоспроизводимые” баги
🧠 Интересный факт
ThreadLocal не хранит данные “в себе”.Наоборот:
👉 каждый
Thread хранит ThreadLocalMapТо есть данные лежат внутри потока, а не объекта
ThreadLocal.Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍1
Enum.Внутренне
EnumMap использует массив для хранения значений, где индекс массива соответствует порядковому номеру (ordinal()) элемента перечисления. Это делает его быстрее, чем хэш-таблицы (HashMap) и более экономным по памяти.EnumMap хранит ключи в порядке их объявления в перечислении, что отличает его от большинства других Map (кроме LinkedHashMap).Так как ключи строго ограничены перечислением, это снижает вероятность ошибок при разработке.
🔍 Пример:
import java.util.EnumMap;
enum Action {
START, STOP, PAUSE
}
public class EnumMapExample {
public static void main(String[] args) {
EnumMap<Action, Runnable> actionMap = new EnumMap<>(Action.class);
// Определяем поведение для каждого значения Enum
actionMap.put(Action.START, () -> System.out.println("Starting the process..."));
actionMap.put(Action.STOP, () -> System.out.println("Stopping the process..."));
actionMap.put(Action.PAUSE, () -> System.out.println("Pausing the process..."));
// Пример вызова
Action currentAction = Action.START;
actionMap.get(currentAction).run();
}
}
#Java #EnumMap
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤2👏1
Когда разработчики впервые видят:
list.parallelStream()
возникает мысль: О, бесплатная многопоточность 🚀
Но на практике
parallelStream() очень часто:❗ НЕ ускоряет код
❗ делает его медленнее
❗ а иногда ещё и ломает логику
📦 Как это работает
list.parallelStream()
.map(this::process)
.toList();
Java разбивает задачи между потоками через
ForkJoinPool.commonPool().🔥 Когда это реально помогает
✔️ CPU-heavy вычисления
✔️ большие объёмы данных
✔️ независимые операции
Например:
✅ обработка миллионов чисел
✅ сложные математические вычисления
✅ image processing
⚠️ Когда будет хуже
❌ Маленькие коллекции
List.of(1, 2, 3)
.parallelStream()
👉 накладные расходы на потоки будут больше пользы
❌ I/O операции
users.parallelStream()
.map(this::callApi)
👉 потоки начинают ждать сеть/БД
👉
ForkJoinPool забивается❌ Общие mutable-данные
List<String> result = new ArrayList<>();
list.parallelStream()
.forEach(result::add);
💣 Race condition detected 🙂
❌ Неочевидный порядок
parallelStream().forEach(System.out::println);
Порядок вывода будет случайным.
🧠 Самая скрытая проблема
parallelStream() использует ОБЩИЙ pool потоков.То есть:
▸ твой код
▸ библиотека
▸ другой модуль приложения
могут конкурировать за одни и те же потоки.
👉 Особенно весело в backend-приложениях.
🚀 Иногда обычный stream быстрее
Почему?
Потому что parallel:
▸ делит задачи
▸ синхронизирует результаты
▸ координирует потоки
Это тоже стоит времени.
✅ Хорошее правило
Используй
parallelStream() только если:▸ операция тяжёлая
▸ данных много
▸ нет shared mutable state
▸ ты реально измерил performance
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1💯1
Класс
Desktop используется для взаимодействия с приложениями операционной системы, такими как веб-браузер, почтовый клиент, просмотрщик изображений и т.д. Этот класс входит в пакет java.awt и позволяет, например, открыть веб-ссылку в браузере или отправить письмо через почтовый клиент.Основные возможности класса Desktop:
✔️
browse(URI uri) – открывает URI (например, веб-страницу) в браузере.✔️
open(File file) – открывает файл в приложении, ассоциированном с его типом (например, текстовый файл в текстовом редакторе).✔️
edit(File file) – открывает файл в режиме редактирования (если доступно).✔️
mail(URI mailtoURI) – открывает почтовое приложение с указанным URI.✔️
print(File file) – отправляет файл на печать.Пример:
import java.awt.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class DesktopExample {
public static void main(String[] args) throws IOException, URISyntaxException {
// Проверяем, поддерживает ли система класс Desktop
if (Desktop.isDesktopSupported()) {
Desktop desktop = Desktop.getDesktop();
// Пример: Открытие веб-страницы
URI uri = new URI("http://www.google.com");
desktop.browse(uri);
} else {
System.out.println("Класс Desktop не поддерживается на этой системе.");
}
}
}
Проверка
Desktop.isDesktopSupported() обязательна, так как класс может не поддерживаться на некоторых системах.#java #Desktop
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤1💯1
Современные приложения все чаще требуют выполнения нескольких задач одновременно, и для этого Java предоставляет мощный инструмент — Concurrency API (набор классов и интерфейсов). Этот API позволяет разработчикам легко реализовывать многопоточность, управлять потоками и синхронизировать действия между ними, что значительно увеличивает производительность и отзывчивость приложений.
Основные классы:
1. Thread
- Класс для создания и управления потоками. Вы можете создать новый поток, реализовав интерфейс
Runnable или расширив класс Thread.2. Executor
- Интерфейс для управления потоками и выполнения задач. Позволяет абстрагироваться от управления потоками, сосредоточившись на логике приложения.
- ExecutorService: расширение Executor, управляющее жизненным циклом потоков.
3. Future
- Позволяет получать результаты из асинхронных задач. Используется в связке с
ExecutorService для выполнения задач в фоновом режиме.4. CountDownLatch
- Синхронизирует потоки, позволяя одному или нескольким потокам ждать завершения других потоков перед продолжением работы.
5. CyclicBarrier
- Используется для синхронизации группы потоков. Позволяет потоку ждать, пока все другие не достигнут определенной точки.
6. Semaphore
- Контролирует доступ к ресурсу, предоставляя определенное количество разрешений для потоков.
7. BlockingQueue
- Интерфейс, предоставляющий безопасный способ обмена данными между потоками при помощи очередей. Реализации включают
ArrayBlockingQueue, LinkedBlockingQueue и другие.#java #ConcurrencyAPI #Thread
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3🔥2
До Java 13 работа с многострочными строками была болью. JSON? HTML? SQL? Только через
"\n" + и кучу экранирования.Но с Text Blocks всё стало проще, понятнее и читаемо.
Сравни:
String html = "<html>\n" +
" <body>Hello</body>\n" +
"</html>";
String html = """
<html>
<body>Hello</body>
</html>
""";
String, просто оформленная красиво.Text Blocks:1.
Код больше не превращается в кашу. Особенно полезно для:
SQL-запросов:
String query = """
SELECT id, name
FROM users
WHERE active = true
ORDER BY created_at DESC
""";
HTML/JSON шаблонов:
String json = """
{
"name": "Alice",
"role": "admin"
}
""";
2.
Забудь про
\", \\n, \\t — теперь можно писать почти как в блокноте.3.
Java сама уберёт начальные отступы на основе самой "узкой" строки.
Пример:
String msg = """
Line 1
Line 2
Line 3
""";
Line 1
Line 2
Line 3
4.
.formatted() Нельзя вставить переменные прямо в Text Block?
Используем .formatted() — коротко и читабельно:
String user = "Bob";
String template = """
{
"user": "%s",
"access": "granted"
}
""".formatted(user);
````
```java
String s = """
Hello""";
System.out.println(s.length()); // 6, а не 5 (есть \n)
3.
Нельзя писать так:
String name = "Eve";
String wrong = """
Hello, $name!
"""; // ❌ не сработает
Вместо этого:
String right = """
Hello, %s!
""".formatted(name);
+ "\n".Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤1🔥1
Java-приложение в Docker
В современном мире разработки программного обеспечения Docker становится важным инструментом, помогающим разрабатывать, тестировать и развертывать приложения в изолированной среде. Использование Docker с Java-приложениями позволяет значительно упростить процесс развертывания, гарантируя, что ваше приложение будет работать одинаково на всех средах.
Docker — это платформа для создания, развертывания и управления контейнерами. Контейнеры изолируют приложения и все их зависимости, что делает возможным запуск кода в любом окружении без конфликта с другими приложениями.
Шаг 1: Установка Docker
Перед тем как начать, убедитесь, что Docker установлен на вашем компьютере. Вы можете следовать [официальной документации](https://docs.docker.com/get-docker/) для установки на вашу операционную систему.
Шаг 2: Создание Java-приложения
Для примера создадим простое приложение. Предположим, у вас есть простая программа, которая выводит "Hello, World!".
Создайте файл
Скомпилируйте её с помощью:
Шаг 3: Создание Dockerfile
Dockerfile — это текстовый файл, позволяющий автоматически собирать образ Docker. Создайте файл с именем
Шаг 4: Сборка Docker-образа
Теперь создайте Docker-образ, используя следующую команду в каталоге вашего проекта:
Эта команда соберет образ и назовет его
Шаг 5: Запуск контейнера
Чтобы запустить контейнер, выполните команду:
Вы должны увидеть вывод:
Флаг
#java #docker
В современном мире разработки программного обеспечения Docker становится важным инструментом, помогающим разрабатывать, тестировать и развертывать приложения в изолированной среде. Использование Docker с Java-приложениями позволяет значительно упростить процесс развертывания, гарантируя, что ваше приложение будет работать одинаково на всех средах.
Docker — это платформа для создания, развертывания и управления контейнерами. Контейнеры изолируют приложения и все их зависимости, что делает возможным запуск кода в любом окружении без конфликта с другими приложениями.
Шаг 1: Установка Docker
Перед тем как начать, убедитесь, что Docker установлен на вашем компьютере. Вы можете следовать [официальной документации](https://docs.docker.com/get-docker/) для установки на вашу операционную систему.
Шаг 2: Создание Java-приложения
Для примера создадим простое приложение. Предположим, у вас есть простая программа, которая выводит "Hello, World!".
Создайте файл
HelloWorld.java:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Скомпилируйте её с помощью:
javac HelloWorld.java
Шаг 3: Создание Dockerfile
Dockerfile — это текстовый файл, позволяющий автоматически собирать образ Docker. Создайте файл с именем
Dockerfile в каталоге вашего проекта:
# Используем официальный образ OpenJDK
FROM openjdk:17-jdk-slim
# Устанавливаем рабочий каталог
WORKDIR /app
# Копируем скомпилированный класс в контейнер
COPY HelloWorld.class ./
# Определяем команду запуска
CMD ["java", "HelloWorld"]
Шаг 4: Сборка Docker-образа
Теперь создайте Docker-образ, используя следующую команду в каталоге вашего проекта:
docker build -t helloworld .
Эта команда соберет образ и назовет его
helloworld.Шаг 5: Запуск контейнера
Чтобы запустить контейнер, выполните команду:
docker run --rm helloworld
Вы должны увидеть вывод:
Hello, World!
Флаг
--rm автоматически удаляет контейнер после его остановки.#java #docker
👍5👏1
Лямбда-выражения могут захватывать переменные из внешнего окружения, делая их доступными внутри лямбда-функции. При этом переменные могут быть:
1️⃣ Неизменяемыми (effectively final) – Переменная из внешнего контекста, используемая в лямбде, должна быть объявлена как
final или фактически быть неизменяемой (то есть не изменяться после первого присваивания). Например:
int x = 10;
Runnable r = () -> System.out.println(x); // x захвачен в лямбде
2️⃣ Свободными от изменения в лямбде – Лямбда не может изменять захваченные переменные. Это ограничение гарантирует, что нет неоднозначного состояния, когда переменная изменяется из нескольких мест (например, из основного потока и из лямбда-функции одновременно).
3️⃣ Статическими или полями класса – В отличие от локальных переменных, статические поля класса или поля экземпляра могут свободно изменяться внутри лямбда-выражений, поскольку их значения хранятся в куче (heap) и доступны по ссылке.
Пример:
public class Main {
private static int staticVar = 20;
public static void main(String[] args) {
int localVar = 10;
Runnable r = () -> System.out.println(localVar + staticVar);
r.run();
}
}
В этом примере
localVar захватывается, так как он effectively final, а staticVar доступен, так как это статическое поле.#java #lambda #capturing
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤2
public class CommandLine {
public static void main(String[] args) throws IOException {
Runtime rt = Runtime.getRuntime();
String[] commands = {"ping", "-c 5", "google.com"};
Process proc = rt.exec(commands);
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));
// Read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// Read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
}
}
Метод
Runtime.getRuntime() возвращает объект, представляющий текущую среду выполнения Java. Этот объект позволяет запускать команды системы.Метод
exec() запускает системную команду и возвращает объект Process, который представляет запущенный процесс. Команда, которую нужно выполнить, передается как массив строк.Далее получаем поток для чтения данных из стандартного вывода процесса, который был запущен и выводим на консоль. Так же поступаем и с потоком вывода ошибок.
Вывод:
Here is the standard output of the command:
PING google.com (173.194.222.138): 56 data bytes
64 bytes from 173.194.222.138: icmp_seq=0 ttl=60 time=39.479 ms
64 bytes from 173.194.222.138: icmp_seq=1 ttl=60 time=39.753 ms
64 bytes from 173.194.222.138: icmp_seq=2 ttl=60 time=47.982 ms
64 bytes from 173.194.222.138: icmp_seq=3 ttl=60 time=39.569 ms
64 bytes from 173.194.222.138: icmp_seq=4 ttl=60 time=39.850 ms
--- google.com ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 39.479/41.327/47.982/3.330 ms
Here is the standard error of the command (if any):
#java #Runtime #exec
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤1
Есть несколько способов добавить элемент в середину массива. Однако, поскольку массивы имеют фиксированную длину, напрямую добавить элемент в существующий массив нельзя — нужно создавать новый массив с увеличенной длиной и копировать данные. Рассмотрим несколько способов:
1️⃣ Копирование вручную
Суть в том что бы создать новый массив на 1 элемент больше оригинального и перенести элементы из старого, не забыв при этом вставить новый элемент.
int[] array = {1, 2, 4, 5};
int index = 2; // индекс, куда вставить новый элемент
int newElement = 3;
// Создаем новый массив на 1 больше
int[] newArray = new int[array.length + 1];
// Копируем элементы до позиции вставки
for (int i = 0; i < index; i++) {
newArray[i] = array[i];
}
// Вставляем новый элемент
newArray[index] = newElement;
// Копируем оставшиеся элементы
for (int i = index + 1; i < newArray.length; i++) {
newArray[i] = array[i - 1];
}
// Печатаем новый массив
System.out.println(Arrays.toString(newArray)); // [1, 2, 3, 4, 5]
2️⃣ Использование коллекций (например, ArrayList)
ArrayList динамический по размеру, и можно легко вставить элемент в любое место с помощью метода add(index, element).
Integer[] array = {1, 2, 4, 5};
int index = 2; // индекс, куда вставить новый элемент
int newElement = 3;
List<Integer> list = new ArrayList<>(Arrays.asList(array));
// Вставляем элемент
list.add(index, newElement);
// Создаем новый массив на 1 больше
Integer[] newArray = list.toArray(array);
// Печатаем новый массив
System.out.println(Arrays.toString(newArray)); // [1, 2, 3, 4, 5]
3️⃣ Использование метода System.arraycopy
Метод
System.arraycopy позволяет эффективно копировать части массива.
int[] array = {1, 2, 4, 5};
int index = 2; // индекс, куда вставить новый элемент
int newElement = 3;
// Создаем новый массив на 1 больше
int[] newArray = new int[array.length + 1];
// Копируем элементы до позиции вставки
System.arraycopy(array, 0, newArray, 0, index);
// Вставляем новый элемент
newArray[index] = newElement;
// Копируем оставшиеся элементы
System.arraycopy(array, index, newArray, index + 1, array.length - index);
// Печатаем новый массив
System.out.println(Arrays.toString(newArray)); // [1, 2, 3, 4, 5]
#java #array
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🔄 Бесконечные потоки
Интерфейс Stream имеет два статических метода для генерации бесконечных потоков:
iterate
generate
📌 При работе с бесконечными потоками, крайне важно вызвать метод limit() перед вызовом терминальной операции, иначе наша программа будет работать бесконечно.
Интерфейс Stream имеет два статических метода для генерации бесконечных потоков:
iterate() и generate().iterate
(final T seed, final UnaryOperator<T> f) возвращает бесконечный последовательный упорядоченный поток, созданный путем итеративного применения функции f к исходному элементу начального значения, создавая поток, состоящий из начального числа, f(начальное число), f(f(начальное число)) и т. д.generate
(Supplier<? extends T> s) возвращает бесконечный последовательный неупорядоченный поток, в котором каждый элемент создается предоставленным поставщиком (Supplier). Это подходит для генерации константных потоков, потоков случайных элементов и т. д.📌 При работе с бесконечными потоками, крайне важно вызвать метод limit() перед вызовом терминальной операции, иначе наша программа будет работать бесконечно.
👍2❤1
Пример POJO:
public class Person {
private String name;
private int age;
// Конструктор
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Геттеры и сеттеры
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
POJO используется для создания простых объектов без привязки к какой-либо специфической архитектуре или фреймворку. Например, в JPA объекты-сущности часто являются POJO, что позволяет их использовать независимо от платформы.
#java #pojo
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Когда требуется конкатенировать строки с использованием
Stream, можно выбрать один из двух методов: Stream.reduce() или Stream.collect(Collectors.joining()).Пример с
Stream.reduce(): List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().reduce("", (a, b) -> a + b);
System.out.println(result); // Str1Str2Str3Пример с
Collectors.joining(): List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().collect(Collectors.joining());
System.out.println(result); // Str1Str2Str3Использование
reduce() для конкатенации строк не является оптимальным с точки зрения производительности. При каждом вызове операции +, создается новая строка, так как строки в Java неизменяемы. Это приводит к увеличению нагрузки на память из-за создания множества промежуточных объектов.В свою очередь, метод
Collectors.joining() использует StringBuilder для сборки строк, что значительно эффективнее. Он избегает создания лишних объектов и снижает потребление памяти.#java #Stream #reduce #joining
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
java.util.concurrent.Использует подход "разделяй и властвуй", где задача разбивается на подзадачи до тех пор, пока они не станут достаточно маленькими для последовательного решения. Для этого используются классы
RecursiveTask<V> (возвращает результат) и RecursiveAction (без результата).ForkJoinPool динамически управляет количеством потоков, при необходимости создавая новые. Обычно это количество соответствует числу процессоров, доступных в системе.
Использует технику work-stealing, где потоки, завершившие свои задачи, могут "красть" задачи у других потоков, чтобы эффективно использовать ресурсы процессора.
ForkJoinPool обладает высокой производительностью для задач, которые можно разбить на независимые подзадачи.
Основные методы:
invoke(): синхронно запускает задачу и ждет её завершения.
submit(): запускает задачу асинхронно.
execute(): также запускает задачу асинхронно, но не возвращает результат.
Пример использования:
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
class SumTask extends RecursiveTask<Integer> {
private final int[] array;
private final int start, end;
private final int threshold = 10;
public SumTask(int[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if (end - start <= threshold) {
// Базовый случай: небольшая задача
int sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
} else {
// Разделяем задачу
int mid = (start + end) / 2;
SumTask leftTask = new SumTask(array, start, mid);
SumTask rightTask = new SumTask(array, mid, end);
leftTask.fork(); // Асинхронно запускаем левую подзадачу
int rightResult = rightTask.compute(); // Синхронно вычисляем правую подзадачу
int leftResult = leftTask.join(); // Ждем завершения левой подзадачи
return leftResult + rightResult;
}
}
}
public class ForkJoinExample {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
SumTask task = new SumTask(array, 0, array.length);
int result = pool.invoke(task);
System.out.println("Сумма: " + result);
}
}
Этот код создает задачу для суммирования массива, разбивая его на подзадачи.
#java #ForkJoinPool
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Последовательные потоки (Sequential Streams)
✔️ Обрабатывают элементы последовательно, один за другим, на одном потоке, следовательно производительность ограничена возможностями одного ядра процессора.
✔️ Используют основной поток программы (main thread) для выполнения операций.
✔️ Метод stream() создаёт последовательный поток.
Пример последовательного потока:
List<String> list = List.of("Hello ", "w", "o", "r", "l", "d!");
list.stream().forEach(System.out::print);
// Hello world!
Параллельные потоки (Parallel Streams)
✔️ Обрабатывают элементы параллельно, распределяя их между несколькими потоками (threads), что позволяет использовать многопоточность.
✔️ Используют ForkJoinPool для распределения задач между потоками.
✔️ Эффективны для больших наборов данных, поскольку могут улучшить производительность на многоядерных процессорах.
✔️ Параллельный поток можно создать, вызвав метод parallelStream() или применив метод parallel() к уже существующему потоку.
Пример параллельного потока:
List<String> list = List.of("Hello ", "w", "o", "r", "l", "d!");
list.parallelStream().forEach(System.out::print);
// rlHello d!wo
#java #Stream #parallelStream
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
Привет, на связи Таня Коровкина из ШОРТКАТ. Ментор по алгоритмам и backend-разработчик
Каждый месяц тысячи разработчиков совершают одни и те же ошибки на алгоритмических интервью 🚩
И продолжают готовиться... не к тому.
6 июля(понедельник) в 19:00 (МСК) проведу вебинар и покажу, что на самом деле оценивает интервьюер и какие ошибки чаще всего приводят к отказу
• дам практические советы, которые можно использовать уже на следующем собеседовании
• расскажу про специфику российского BigTech
🤘 Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot
Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM