Проверяем пересечение временных интервалов на Java!
Нужно понять, конфликтует ли новый слот с уже существующими. Такая задача встречается в календарях, бронированиях, расписаниях задач и системах записи.
В этой задаче:
Вместо набора частных случаев появляется одно универсальное условие.
👉 Java Ready | #задача
Нужно понять, конфликтует ли новый слот с уже существующими. Такая задача встречается в календарях, бронированиях, расписаниях задач и системах записи.
В этой задаче:
• описываем интервал отдельным record
• пишем короткое правило пересечения
• фильтруем занятые слоты через stream
• получаем список конфликтов
Вместо набора частных случаев появляется одно универсальное условие.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3🔥3
Знали, почему Pattern.compile лучше не вызывать в горячем цикле?
В Java регулярные выражения часто используют для валидации, поиска и очистки строк. Рабочий код выглядит так:
Проблема в том, что Pattern.compile создаёт объект шаблона. Если делать это на каждой итерации, программа снова и снова разбирает одно и то же выражение.
Для короткого списка разницы почти не видно. Но в обработке логов, CSV, API-ответов или очередей это превращается в лишнюю нагрузку.
Лучше скомпилировать шаблон один раз:
А внутри цикла только создавать matcher:
Так код показывает, что регулярка постоянная, а не строится заново для каждой строки.
Если шаблон зависит от входных данных, его всё равно можно кешировать по ключу:
Но кешировать стоит только ограниченный набор шаблонов, чтобы не получить бесконечно растущую Map.
👉 Java Ready | #совет
В Java регулярные выражения часто используют для валидации, поиска и очистки строк. Рабочий код выглядит так:
for (String email : emails) {
if (Pattern.compile(".+@.+\\..+").matcher(email).matches()) {
valid.add(email);
}
}Проблема в том, что Pattern.compile создаёт объект шаблона. Если делать это на каждой итерации, программа снова и снова разбирает одно и то же выражение.
Для короткого списка разницы почти не видно. Но в обработке логов, CSV, API-ответов или очередей это превращается в лишнюю нагрузку.
Лучше скомпилировать шаблон один раз:
private static final Pattern EMAIL =
Pattern.compile(".+@.+\\..+");
А внутри цикла только создавать matcher:
for (String email : emails) {
if (EMAIL.matcher(email).matches()) {
valid.add(email);
}
}Так код показывает, что регулярка постоянная, а не строится заново для каждой строки.
Если шаблон зависит от входных данных, его всё равно можно кешировать по ключу:
Pattern pattern = cache.computeIfAbsent(mask, Pattern::compile);
Но кешировать стоит только ограниченный набор шаблонов, чтобы не получить бесконечно растущую Map.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4🔥4🤝1
Java 25 становится проще!
Автор разбирает JEP-512 и изменения, которые позволяют писать небольшие программы с меньшим количеством обязательной обвязки. Вместо класса, static main и ручных импортов можно сосредоточиться на самой задаче, а не на стартовом шаблоне.
В статье разбирают:
• как устроены упрощённые исходники Java 25
• зачем появились неявные импорты и класс IO
• как запускать короткие Java-скрипты из командной строки
👉 Java Ready | #статья
Автор разбирает JEP-512 и изменения, которые позволяют писать небольшие программы с меньшим количеством обязательной обвязки. Вместо класса, static main и ручных импортов можно сосредоточиться на самой задаче, а не на стартовом шаблоне.
В статье разбирают:
• как устроены упрощённые исходники Java 25
• зачем появились неявные импорты и класс IO
• как запускать короткие Java-скрипты из командной строки
Продолжай читать на Habr
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍5🔥3
Почему Files.lines() нельзя оставлять без try-with-resources?
Files.lines() выглядит как удобный способ быстро получить Stream строк из файла. Но за этим Stream остаётся открытый файловый ресурс.
После count поток может быть исчерпан, но файл не обязан закрыться сразу. В долгоживущем сервисе или цикле обработки это приводит к накоплению открытых дескрипторов.
Правильнее ограничить жизнь Stream блоком try-with-resources.
Java закроет поток даже при исключении внутри обработки. Это особенно полезно для больших логов, CSV-файлов и пакетных задач, где не хочется загружать всё содержимое в память.
Если нужен небольшой файл целиком, можно взять готовый список строк.
readAllLines проще, но читает файл полностью. Files.lines подходит для потоковой обработки, если не забывать закрывать ресурс.
👉 Java Ready | #совет
Files.lines() выглядит как удобный способ быстро получить Stream строк из файла. Но за этим Stream остаётся открытый файловый ресурс.
Stream<String> lines = Files.lines(path);
long count = lines.count();
После count поток может быть исчерпан, но файл не обязан закрыться сразу. В долгоживущем сервисе или цикле обработки это приводит к накоплению открытых дескрипторов.
Правильнее ограничить жизнь Stream блоком try-with-resources.
try (Stream<String> lines = Files.lines(path)) {
long count = lines.count();
}Java закроет поток даже при исключении внутри обработки. Это особенно полезно для больших логов, CSV-файлов и пакетных задач, где не хочется загружать всё содержимое в память.
Если нужен небольшой файл целиком, можно взять готовый список строк.
List<String> lines = Files.readAllLines(path);
readAllLines проще, но читает файл полностью. Files.lines подходит для потоковой обработки, если не забывать закрывать ресурс.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍5🔥4
Напоминалка по устройству Java records!
На картинке показано, из каких частей состоит record и что генерирует компилятор.
Например, запись public record Person(String name, int age) создаёт final-поля, канонический конструктор, методы name(), age(), equals() и hashCode() без ручного шаблонного кода.
Сохрани, чтобы не потерять!
👉 Java Ready | #совет
На картинке показано, из каких частей состоит record и что генерирует компилятор.
Например, запись public record Person(String name, int age) создаёт final-поля, канонический конструктор, методы name(), age(), equals() и hashCode() без ручного шаблонного кода.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤6🔥6
Запускаем внешнюю команду через ProcessBuilder!
Иногда Java-приложению нужно вызвать системную утилиту, например git, ffmpeg или собственный скрипт деплоя. Склеивать такую команду в одну строку опасно, особенно если параметры приходят извне.
ProcessBuilder принимает аргументы отдельно. Так пробелы и специальные символы не превращаются в случайный shell-код.
redirectErrorStream(true) объединяет обычный вывод и ошибки. Это не даёт процессу зависнуть, когда буфер stderr заполняется, пока приложение читает только stdout.
После чтения обязательно дождитесь завершения и проверьте код возврата. Нулевой код обычно означает успех.
Для долгих команд добавьте waitFor с таймаутом и уничтожайте процесс при превышении лимита. Так интеграция с внешним инструментом не сможет навсегда заблокировать поток приложения.
👉 Java Ready | #практика
Иногда Java-приложению нужно вызвать системную утилиту, например git, ffmpeg или собственный скрипт деплоя. Склеивать такую команду в одну строку опасно, особенно если параметры приходят извне.
ProcessBuilder принимает аргументы отдельно. Так пробелы и специальные символы не превращаются в случайный shell-код.
var process = new ProcessBuilder(
"git", "status", "--short")
.redirectErrorStream(true)
.start();
redirectErrorStream(true) объединяет обычный вывод и ошибки. Это не даёт процессу зависнуть, когда буфер stderr заполняется, пока приложение читает только stdout.
try (var reader = process.inputReader()) {
var lines = reader.lines().toList();
lines.forEach(System.out::println);
}После чтения обязательно дождитесь завершения и проверьте код возврата. Нулевой код обычно означает успех.
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IllegalStateException("Command failed");
}
Для долгих команд добавьте waitFor с таймаутом и уничтожайте процесс при превышении лимита. Так интеграция с внешним инструментом не сможет навсегда заблокировать поток приложения.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥3
Ищем одинаковые файлы на Java!
В папке могут лежать копии одного файла под разными именами. Сделаем небольшую утилиту, которая сравнивает содержимое, а не названия.
В этой задаче:
Результат поможет разобрать архивы, папки и копии.
👉 Java Ready | #задача
В папке могут лежать копии одного файла под разными именами. Сделаем небольшую утилиту, которая сравнивает содержимое, а не названия.
В этой задаче:
• обходим вложенные папки
• считаем SHA-256 потоково
• группируем пути по хешу
• показываем кандидатов на дубликаты
Результат поможет разобрать архивы, папки и копии.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍5🔥4
Почему trim() может не убрать пробелы по краям строки?
В Java часто очищают пользовательский ввод так:
Но trim() ориентируется на символы с кодом не выше U+0020. Некоторые пробельные символы Unicode под это правило не попадают.
Например, строка может начинаться и заканчиваться широким пробелом U+2003:
Видимые на экране отступы останутся. Это может сломать сравнение логина, поиск ключа или проверку поля формы.
Начиная с Java 11 есть strip(), который опирается на определение пробельного символа в Character.isWhitespace:
Если нужна очистка только с одной стороны, есть отдельные методы:
Для проверки строки, состоящей только из пробелов, пригодится isBlank():
strip() не удаляет все символы, которые выглядят как пробелы. Для специальных форматов ввода полезно отдельно определить допустимые символы.
👉 Java Ready | #совет
В Java часто очищают пользовательский ввод так:
String name = input.trim();
Но trim() ориентируется на символы с кодом не выше U+0020. Некоторые пробельные символы Unicode под это правило не попадают.
Например, строка может начинаться и заканчиваться широким пробелом U+2003:
String input = "\u2003admin\u2003";
System.out.println(input.trim().length());
Видимые на экране отступы останутся. Это может сломать сравнение логина, поиск ключа или проверку поля формы.
Начиная с Java 11 есть strip(), который опирается на определение пробельного символа в Character.isWhitespace:
String name = input.strip();
System.out.println(name.equals("admin"));
Если нужна очистка только с одной стороны, есть отдельные методы:
String left = input.stripLeading();
String right = input.stripTrailing();
Для проверки строки, состоящей только из пробелов, пригодится isBlank():
if (input.isBlank()) {
throw new IllegalArgumentException("Empty name");
}strip() не удаляет все символы, которые выглядят как пробелы. Для специальных форматов ввода полезно отдельно определить допустимые символы.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍4🔥4
Собираем Tower Defense с JavaFX и FXGL!
Автор решил попробовать FXGL на проекте и начал с игры, в которой враги идут по маршруту, а игрок расставляет башни. Получился не только экран с графикой, но и небольшой игровой цикл с ресурсами, волнами и улучшениями.
В статье показывают:
• как разложены игровые сущности и компоненты
• что происходит при размещении башни на поле
• как игра проверяет стоимость и доступность покупки
👉 Java Ready | #статья
Автор решил попробовать FXGL на проекте и начал с игры, в которой враги идут по маршруту, а игрок расставляет башни. Получился не только экран с графикой, но и небольшой игровой цикл с ресурсами, волнами и улучшениями.
В статье показывают:
• как разложены игровые сущности и компоненты
• что происходит при размещении башни на поле
• как игра проверяет стоимость и доступность покупки
Продолжай читать на Habr
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2🔥2
Почему kubectl apply ещё не означает, что приложение заработало?
Во время деплоя часто запускают одну команду и считают её успешное завершение концом работы:
Но apply сообщает, что Kubernetes принял описание ресурса. Новые Pod могут ещё создаваться, скачивать образ, ждать readiness probe или падать при запуске.
После apply проверьте именно завершение обновления Deployment:
Команда ждёт завершения rollout и возвращает ошибку, если не дождалась его за указанное время. Название app здесь должно совпадать с именем вашего Deployment.
В скрипте команды удобно поставить рядом:
Теперь пайплайн не продолжит работу после неуспешного ожидания. Само по себе это всё ещё не заменяет проверку приложения снаружи кластера. Сервис, ingress и внешние зависимости могут ломаться отдельно.
Если обновление застряло, сначала посмотрите состояние ресурсов:
Затем откройте события Deployment и причину проблемы у конкретного Pod:
Проблемой может оказаться неверный образ, недоступный секрет, ошибка readiness probe или нехватка ресурсов.
Предыдущую ревизию можно вернуть отдельно:
После отката тоже проверьте rollout status.
➡️ DevOps Ready | #совет
Во время деплоя часто запускают одну команду и считают её успешное завершение концом работы:
kubectl apply -f deployment.yaml
Но apply сообщает, что Kubernetes принял описание ресурса. Новые Pod могут ещё создаваться, скачивать образ, ждать readiness probe или падать при запуске.
После apply проверьте именно завершение обновления Deployment:
kubectl rollout status deployment/app --timeout=120s
Команда ждёт завершения rollout и возвращает ошибку, если не дождалась его за указанное время. Название app здесь должно совпадать с именем вашего Deployment.
В скрипте команды удобно поставить рядом:
set -e
kubectl apply -f deployment.yaml
kubectl rollout status deployment/app --timeout=120s
Теперь пайплайн не продолжит работу после неуспешного ожидания. Само по себе это всё ещё не заменяет проверку приложения снаружи кластера. Сервис, ingress и внешние зависимости могут ломаться отдельно.
Если обновление застряло, сначала посмотрите состояние ресурсов:
kubectl get deployment app
kubectl get pods -l app=app
Затем откройте события Deployment и причину проблемы у конкретного Pod:
kubectl describe deployment app
kubectl describe pod POD_NAME
Проблемой может оказаться неверный образ, недоступный секрет, ошибка readiness probe или нехватка ресурсов.
Предыдущую ревизию можно вернуть отдельно:
kubectl rollout undo deployment/app
После отката тоже проверьте rollout status.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍2🔥2
Шпаргалка по sealed-иерархиям в Java!
На картинке показано, как sealed-класс ограничивает прямых наследников. Одни ветки закрываются через final, другая остаётся контролируемой через sealed, а non-sealed снова разрешает свободное наследование.
Например, для фиксированного набора фигур можно явно перечислить допустимые реализации Shape. Это помогает выразить границы модели прямо в типах и облегчает проверку вариантов при работе с pattern matching.
Сохрани, чтобы не потерять!
👉 Java Ready | #ресурс
На картинке показано, как sealed-класс ограничивает прямых наследников. Одни ветки закрываются через final, другая остаётся контролируемой через sealed, а non-sealed снова разрешает свободное наследование.
Например, для фиксированного набора фигур можно явно перечислить допустимые реализации Shape. Это помогает выразить границы модели прямо в типах и облегчает проверку вариантов при работе с pattern matching.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍2🔥2
Следим за появлением файлов в папке через WatchService!
Создадим наблюдатель и зарегистрируем каталог. Папка должна существовать заранее:
Константы ENTRY_CREATE и ENTRY_MODIFY берутся из StandardWatchEventKinds. try-with-resources закроет наблюдатель при выходе из блока.
Дальше ждём сигнал. Метод take блокирует поток, пока событие не появится:
Контекст события содержит имя относительно наблюдаемой папки, а не готовый абсолютный путь. Не подставляй его отдельно от dir:
После обработки обязательно сбросим ключ. Если каталог удалён или больше недоступен, reset вернёт false:
События могут повторяться, а при перегрузке наблюдатель сообщит OVERFLOW. Поэтому важную обработку лучше делать идемпотентной и предусмотреть повторное сканирование каталога после переполнения.
👉 Java Ready | #практика
Создадим наблюдатель и зарегистрируем каталог. Папка должна существовать заранее:
Path dir = Path.of("incoming");
try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY);Константы ENTRY_CREATE и ENTRY_MODIFY берутся из StandardWatchEventKinds. try-with-resources закроет наблюдатель при выходе из блока.
Дальше ждём сигнал. Метод take блокирует поток, пока событие не появится:
while (true) {
WatchKey key = watcher.take();
for (WatchEvent<?> event : key.pollEvents()) {Контекст события содержит имя относительно наблюдаемой папки, а не готовый абсолютный путь. Не подставляй его отдельно от dir:
if (event.kind() == OVERFLOW) continue;
Path file = dir.resolve((Path) event.context());
System.out.println(event.kind() + " " + file);
}
После обработки обязательно сбросим ключ. Если каталог удалён или больше недоступен, reset вернёт false:
if (!key.reset()) break;
}
}
События могут повторяться, а при перегрузке наблюдатель сообщит OVERFLOW. Поэтому важную обработку лучше делать идемпотентной и предусмотреть повторное сканирование каталога после переполнения.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍4🔥2