Открытый канал ID 72791201979798

Java Dev

Все про Java и не только

Каналов в рубрике
1 729
Место этого канала
№ 868
Открыть рубрику →

Публикации за период

По реакциям

Поиск и вся лента — после бесплатной регистрации. Свои даты — по подписке; владелец канала видит их бесплатно. Сортировка по реакциям — по подписке. Зарегистрироваться

Загрузка…

HashMap в Java — как хранить данные по ключу?

HashMap — одна из самых популярных коллекций в Java. Она хранит данные в формате ключ → значение и позволяет быстро находить элементы по ключу.

Например, сохраним имена пользователей по их ID:

Map<Integer, String> users = new HashMap<>();

users.put(1, "Alex");
users.put(2, "Maria");
users.put(3, "Ivan");

System.out.println(users.get(2));

Результат:
Maria

Основные методы:
- put(key, value) — добавить или заменить значение.
- get(key) — получить значение по ключу.
- remove(key) — удалить элемент.
- containsKey(key) — проверить наличие ключа.
- size() — узнать количество элементов.

Важные особенности HashMap:
- Ключи уникальны. Если добавить существующий ключ, старое значение заменится новым.
- Допускается один null-ключ и несколько null-значений.
- Порядок элементов не гарантируется.
- HashMap не является потокобезопасной коллекцией.

А что будет здесь?

Map<String, Integer> map = new HashMap<>();

map.put("Java", 10);
map.put("Java", 20);
map.put("Python", 30);

System.out.println(map.size());
System.out.println(map.get("Java"));

Ответ:
2
20

Почему? Повторное добавление ключа "Java" заменяет его значение, а не создаёт новый элемент.

Главная идея: HashMap удобен, когда нужно быстро получать данные по уникальному ключу.
Просмотры: 7
Перейти в пост Открыть в MAX Сообщить о неточности
Stream API — как работать с коллекциями декларативно?

Stream API появился в Java 8 и позволяет обрабатывать коллекции в виде последовательности операций, не изменяя исходную коллекцию.

Например, есть список пользователей:

List<User> users = getUsers();

List<String> names = users.stream()
.filter(User::isActive)
.map(User::getName)
.sorted()
.toList();

Здесь происходит несколько операций:
filter() — оставляет только активных пользователей.
map() — преобразует User в String.
sorted() — сортирует результат.
toList() — собирает элементы в список.

Важная особенность Stream API — операции бывают промежуточными и терминальными.

Промежуточные (filter, map, sorted) формируют pipeline и сами по себе обычно не запускают обработку.
Терминальные (toList, collect, count, forEach, reduce) запускают выполнение stream.

Например:

long count = users.stream()
.filter(User::isActive)
.count();

Stream можно сделать параллельным:

users.parallelStream()
.filter(User::isActive)
.toList();

Но parallelStream() не означает автоматически «быстрее». Для небольших коллекций и простых операций накладные расходы могут оказаться больше выигрыша.

Главная идея: Stream API позволяет описывать, что нужно сделать с данными, а не пошагово управлять каждой операцией.
Просмотры: 15
Перейти в пост Открыть в MAX Сообщить о неточности
CompletableFuture — как выполнять задачи асинхронно в Java?

Если Thread позволяет запустить код в отдельном потоке, то CompletableFuture позволяет удобно строить цепочки асинхронных операций.

Например:

CompletableFuture
.supplyAsync(() -> getUser())
.thenApply(user -> user.getName())
.thenAccept(System.out::println);

Здесь сначала асинхронно получаем пользователя, затем преобразуем результат и передаём его следующему обработчику.

Можно обрабатывать ошибки:

CompletableFuture
.supplyAsync(() -> getUser())
.thenApply(User::getName)
.exceptionally(ex -> "Unknown");

Можно запустить несколько независимых задач параллельно:

var user = getUserAsync();
var orders = getOrdersAsync();

user.thenCombine(orders, (u, o) -> new UserOrders(u, o));

CompletableFuture также позволяет дождаться выполнения нескольких задач:

CompletableFuture.allOf(user, orders).join();

Важно: CompletableFuture — это не отдельный механизм создания потоков. По умолчанию supplyAsync() использует общий ForkJoinPool. При необходимости можно передать собственный Executor.

Главная идея: CompletableFuture удобен, когда нужно строить сложные цепочки асинхронных операций, комбинировать результаты и централизованно обрабатывать ошибки.
Просмотры: 13
Перейти в пост Открыть в MAX Сообщить о неточности
Virtual Threads — почему они изменили многопоточность в Java?

В Java 21 Virtual Threads стали стандартной возможностью. Их главная идея — позволить создавать огромное количество лёгких потоков без необходимости вручную управлять небольшим пулом обычных `Thread`.

Создать виртуальный поток можно очень просто:

Thread.startVirtualThread(() -> {
System.out.println("Hello from virtual thread");
});

Или через ExecutorService:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// HTTP-запрос или другая задача
});
}

Обычный поток ОС (platform thread) — относительно тяжёлый ресурс. Создавать тысячи таких потоков одновременно обычно нецелесообразно.

Virtual Thread значительно легче и хорошо подходит для большого количества задач, которые большую часть времени ждут: HTTP-запросы, работа с базой данных, файловые операции и т.д.

При этом Virtual Threads **не делают CPU-bound задачи быстрее. Если задача активно использует процессор, количество виртуальных потоков само по себе производительность не увеличит.

Главная идея:

Virtual Threads особенно полезны для высококонкурентных приложений с большим количеством блокирующих операций ввода-вывода.

А привычный синхронный код при этом можно сохранить — и это одна из главных особенностей подхода.
Просмотры: 24
Перейти в пост Открыть в MAX Сообщить о неточности
Загружаем изображение…
Java 8 → 27: что нового в каждой версии?

Java активно развивается. Давайте кратко разберём, какие ключевые изменения появились в каждой версии начиная с Java 8.

Java 8 (2014)
• Лямбда-выражения и функциональные интерфейсы.
• Stream API.
• Optional.
• Default-методы в интерфейсах.
• Новое API для работы с датами — java.time.

Java 9 (2017)
• Модульная система Java Platform Module System.
• JShell — интерактивная консоль Java.
• Фабричные методы коллекций: List.of(), Set.of(), Map.of().

Java 10 (2018)
• Локальный вывод типов — var.
• Улучшения сборки мусора.
• Улучшения JIT-компилятора.

Java 11 (2018, LTS)
• Новый HTTP Client API.
• Методы String.isBlank(), lines(), repeat().
• Запуск однофайловых программ без явной компиляции.
• Удаление модулей Java EE и CORBA.

Java 12 (2019)
• Switch Expressions — preview.
• Улучшения сборщика мусора G1.
• Новый сборщик мусора Shenandoah — экспериментальный.

Java 13 (2019)
• Text Blocks — preview.
• Улучшения Switch Expressions.
• Переработка внутреннего API сокетов.

Java 14 (2020)
• Switch Expressions стали стандартом.
• Pattern Matching для instanceof — preview.
• Records — preview.
• Улучшения NullPointerException.

Java 15 (2020)
• Text Blocks стали стандартом.
• Sealed Classes — preview.
• Скрытые классы (Hidden Classes).
• Удалён Nashorn JavaScript Engine.

Java 16 (2021)
• Records стали стандартом.
• Pattern Matching для instanceof стал стандартом.
• Улучшения Stream API и сборщика мусора.

Java 17 (2021, LTS)
• Sealed Classes стали стандартом.
• Pattern Matching и Records получили дальнейшее развитие.
• Новый генератор случайных чисел.
• Удалён экспериментальный AOT-компилятор.

Java 18 (2022)
• UTF-8 стал стандартной кодировкой Java.
• Простой веб-сервер для разработки.
• Улучшения Javadoc.
• Foreign Function & Memory API — incubator.

Java 19 (2022)
• Virtual Threads — preview.
• Structured Concurrency — incubator.
• Record Patterns — preview.
• Pattern Matching для switch — preview.

Java 20 (2023)
• Дальнейшее развитие Virtual Threads.
• Record Patterns и Pattern Matching для switch — новые preview-версии.
• Scoped Values — incubator.

Java 21 (2023, LTS)
• Virtual Threads стали стандартом.
• Record Patterns и Pattern Matching для switch — стандарт.
• Sequenced Collections.
• Structured Concurrency — preview.
• String Templates — preview.

Java 22 (2024)
• Foreign Function & Memory API стал стандартом.
• Unnamed Variables и Patterns.
• Stream Gatherers — preview.
• Улучшения Virtual Threads.

Java 23 (2024)
• Markdown-документация в Javadoc.
• Stream Gatherers — preview.
• Primitive Types в Pattern Matching — preview.
• Улучшения ZGC.

Java 24 (2025)
• Class-File API стал стандартом.
• Stream Gatherers стали стандартом.
• Scoped Values стали стандартом.
• Улучшения Virtual Threads и производительности JVM.

Java 25 (2025, LTS)
• Compact Source Files и Instance Main Methods.
• Module Import Declarations.
• Flexible Constructor Bodies.
• Scoped Values и Key Derivation Function API стали стандартом.

Java 26 (2026)
• HTTP/3 в HTTP Client API.
• Удаление Applet API.
• Улучшения AOT-компиляции.
• Дальнейшее развитие Structured Concurrency.

Java 27 (2026)
• Улучшения производительности JVM и G1 GC.
• Compact Object Headers стали стандартом.
• Улучшения криптографии и TLS.
• Дальнейшее развитие Structured Concurrency.

Главная идея: Java развивается не только за счёт новых конструкций языка, но и благодаря улучшениям JVM, производительности, многопоточности и безопасности.

Примечание: preview и incubator-функции могут изменяться или удаляться до выхода в стандарт.
Просмотры: 32
Перейти в пост Открыть в MAX Сообщить о неточности
Загружаем изображение…
AtomicInteger vs volatile — в чём разница?

В предыдущем посте мы разобрали synchronized и AtomicInteger. Давайте разберёмся, чем AtomicInteger отличается от volatile.

volatile гарантирует, что изменения переменной будут видны другим потокам.

Например:

volatile boolean running = true;

Если один поток изменит running на false, другие потоки увидят это изменение.

Но volatile не гарантирует атомарность операций.

volatile int counter = 0;

counter++;

Операция counter++ состоит из чтения, увеличения и записи. Несколько потоков могут одновременно изменить значение и потерять часть обновлений.

Для таких случаев подходит AtomicInteger:

AtomicInteger counter = new AtomicInteger(0);

counter.incrementAndGet();

Метод incrementAndGet() атомарно увеличивает значение на единицу.

Также доступны другие операции:

counter.get(); // получить значение
counter.set(10); // установить значение
counter.addAndGet(5); // увеличить на 5
counter.compareAndSet(10, 20); // изменить, если текущее значение

равно 10

Главная идея:

volatile обеспечивает видимость изменений, а AtomicInteger позволяет безопасно выполнять атомарные операции над числом без явной блокировки.
Просмотры: 20
Перейти в пост Открыть в MAX Сообщить о неточности
Многопоточность в Java — зачем нужен synchronized?

Когда несколько потоков работают с одними и теми же данными, могут возникнуть проблемы.

Например:

int counter = 0;

void increment() {
counter++;
}

Если этот метод одновременно вызовут несколько потоков, результат может оказаться меньше ожидаемого.

Причина в том, что counter++ — это не одна операция. Упрощённо это:

1. Прочитать значение counter
2. Увеличить его
3. Записать новое значение

Два потока могут одновременно прочитать одно и то же значение и потерять одно из изменений.

Для защиты критической секции используется synchronized:

synchronized void increment() {
counter++;
}

Теперь одновременно выполнять этот метод для одного объекта сможет только один поток.

Можно синхронизировать и отдельный блок:

synchronized (this) {
counter++;
}

Но важно понимать: synchronized не делает весь код автоматически потокобезопасным. Нужно правильно определить, какие данные являются общими и какая именно операция должна быть защищена.

А если синхронизация нужна только для простого счётчика, часто лучше использовать AtomicInteger:

AtomicInteger counter = new AtomicInteger();

counter.incrementAndGet();
Главная идея: многопоточность — это не только запуск нескольких потоков, но и безопасная работа с общими данными.
Просмотры: 23
Перейти в пост Открыть в MAX Сообщить о неточности
Загружаем изображение…
Thread в Java — как запустить код параллельно?

Thread — это поток выполнения внутри процесса. С его помощью можно выполнять несколько задач одновременно.

Самый простой вариант — создать Thread и передать ему Runnable:

Thread thread = new Thread(() -> {
System.out.println("Работаем в отдельном потоке");
});

thread.start();

Важно: start() и run() — не одно и то же.

start() создаёт новый поток и уже в нём вызывает run().

А если написать:

thread.run();

новый поток не создастся — код выполнится в текущем потоке.

Можно создать поток и через наследование:

class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello from thread");
}
}

new MyThread().start();

Но на практике чаще используют Runnable, Callable и ExecutorService, а не создают Thread напрямую.

Например:

ExecutorService executor = Executors.newFixedThreadPool(4);

executor.submit(() -> {
System.out.println("Task executed");
});

executor.shutdown();

Главная идея: Thread — это поток, а ExecutorService позволяет удобно управлять пулом потоков и задачами.
Просмотры: 27
Перейти в пост Открыть в MAX Сообщить о неточности
Вся лента — после бесплатной регистрации
ВКонтакте Телеграм Одноклассники
Избранное в datool.ru

Следите за каналом в личном кабинете

Зарегистрируйтесь в datool.ru, чтобы добавить канал «Java Dev» в избранное и отслеживать его показатели.

Зарегистрироваться
Для владельцев каналов

Подтвердите свой канал

Войдите или зарегистрируйтесь в datool.ru, затем добавьте нашего бота в канал по инструкции в личном кабинете. После проверки владения замок исчезнет и вам станет доступно управление каналом.

Войти или зарегистрироваться

Сообщить о неточности

Опишите, что нужно исправить. Если есть источник, добавьте ссылку.

Вы покидаете datool.ru

Ссылка ведёт на внешний сайт. datool.ru не отвечает за его содержимое — проверьте адрес, прежде чем вводить пароли или платёжные данные.

Переход через 10 с

Перейти