Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Часть 3. Расширение: фильтрация и сортировка жанров

Задача: только жанры классики, отсортированные
public Set<String> getClassicGenresSorted() {
return books.stream()
.filter(book -> book.getYear() < 1950) // только старые книги
.flatMap(book -> book.getGenres().stream())
.filter(genre -> !genre.equals("Фэнтези")) // исключаем современное
.sorted() // сортировка (временно в Stream)
.collect(Collectors.toCollection(LinkedHashSet::new)); // сохраняем порядок
}


Важно: sorted() в промежуточной операции работает, но для больших данных лучше сортировать после collect, если порядок критичен.


Часть 4. Задача с исключением: безопасный парсинг годов

Поступают строки годов из внешнего источника (файл, API, пользовательский ввод). Нужно получить список валидных целых чисел, игнорируя ошибки парсинга.

Входные данные:
["1999", "invalid", "2005", "nineteen", "2020", ""]



Плохой подход:
try-catch в цикле с изменяемым списком
// Не делайте так
List<Integer> years = new ArrayList<>();
for (String s : yearStrings) {
try {
years.add(Integer.parseInt(s));
} catch (NumberFormatException e) {
// игнорируем — но где гарантия, что мы ничего не пропустили?
}
}


Проблемы: изменяемый список, скрытое игнорирование ошибок, сложно тестировать.

Хороший подход: flatMap + Optional
Создайте вспомогательный метод для безопасного парсинга:
// В отдельном классе-утилите или в Library
private static Optional<Integer> safeParseYear(String yearString) {
try {
return Optional.of(Integer.parseInt(yearString));
} catch (NumberFormatException e) {
return Optional.empty();
}
}


Использование в Stream:
public List<Integer> parseValidYears(List<String> yearStrings) {
return yearStrings.stream()
.flatMap(s -> safeParseYear(s).stream()) // Optional → Stream (0 или 1 элемента)
.collect(Collectors.toList());
}


Что происходит:
safeParseYear возвращает Optional<Integer> — явное указание на возможность отсутствия значения
Optional.stream() (Java 9+) создаёт поток из 0 или 1 элемента
flatMap «выпрямляет» потоки: Stream<Optional<Integer>> → Stream<Integer>
Невалидные строки превращаются в пустые потоки и исчезают из результата

Для Java 8 (без Optional.stream()):
.flatMap(s -> safeParseYear(s).map(Stream::of).orElseGet(Stream::empty))



#Java #для_новичков #beginner #stream_api #практика
👍2
Часть 5. Интеграция в проект «Библиотека»

Задача 1: обновление метода добавления книги с валидацией жанров


В Library добавьте проверку, что жанр книги существует в библиотеке:
public boolean isValidGenre(String genre) {
return getAllGenresFunctional().contains(genre);
}

public void addBookWithGenreValidation(Book book) {
for (String genre : book.getGenres()) {
if (!isValidGenre(genre) && !books.isEmpty()) {
System.out.println("Предупреждение: новый жанр '" + genre + "'");
}
}
addBook(book);
}

Улучшение: кэшировать getAllGenresFunctional() при частых проверках.

Задача 2: статистика по жанрам

public Map<String, Long> getGenreStatistics() {
return books.stream()
.flatMap(book -> book.getGenres().stream())
.collect(Collectors.groupingBy(
genre -> genre,
Collectors.counting()
));
}

Результат: { "Роман"=15, "Детектив"=8, "Фантастика"=12 }

Задача 3: книги с неизвестными годами

Используем safeParseYear для обработки внешних данных при импорте:
public List<Book> importBooksFromRawData(List<String[]> rawData) {
return rawData.stream()
.flatMap(row -> {
Optional<String> title = Optional.ofNullable(row[0]);
Optional<String> author = Optional.ofNullable(row[1]);
Optional<Integer> year = safeParseYear(row[2]);
List<String> genres = row.length > 3
? Arrays.asList(row[3].split(","))
: Collections.emptyList();

// создаём книгу только если все обязательные поля валидны
return title.flatMap(t ->
author.flatMap(a ->
year.map(y -> new Book(t, a, y, genres))
)
).stream();
})
.collect(Collectors.toList());
}



Практические задания


Задача 1: рефакторинг существующего кода


Найдите в проекте «Библиотека» методы, которые:
собирают данные в Set или List через циклы и add/addAll
фильтруют коллекции вручную
Перепишите их через flatMap, filter, collect. Добавьте peek для отладки, затем удалите.

Задача 2: поиск книг по нескольким жанрам

Реализуйте метод findBooksByAnyGenre(List<String> targetGenres) — книги, имеющие хотя бы один жанр из списка.
Подсказка: anyMatch внутри filter.
Реализуйте метод findBooksByAllGenres(List<String> targetGenres) — книги, имеющие все жанры из списка.
Подсказка: containsAll или targetGenres.stream().allMatch(...).

Задача 3: отчёт по авторам и их жанрам

Создайте Map<String, Set<String>> — автор → все жанры его книг.
// Ожидаемый результат:
// { "Толстой"=["Роман", "Исторический"],
// "Оруэлл"=["Антиутопия", "Сатира"] }

Используйте flatMap для «размножения» пары (автор, жанр), затем groupingBy.

Задача 4: импорт с валидацией (звёздочка)

Создайте метод importFromCsv(List<String> lines), где каждая строка: "title;author;year;genre1,genre2".

Требования:
пропускать строки с невалидным годом (через safeParseYear)
пропускать строки с пустым названием или автором
собрать статистику: сколько строк успешно, сколько с ошибкой
вернуть ImportResult с List<Book> и List<String> errors
Используйте flatMap для преобразования строки в Optional<Book> → Stream<Book>.


#Java #для_новичков #beginner #stream_api #практика
👍3
Что выведет код?

public class Task260225 {
public static void main(String[] args) {
float a = 0.1f;
double b = 0.1;

System.out.println(a == 0.1);
System.out.println(b == 0.1);
System.out.println(a == 0.1f);
System.out.println(a == (float)0.1);
}
}


#Tasks
👍2
Что такое сериализация (Serialization)? 🤓

Ответ:

Сериализация
— это процесс преобразования состояния объекта в поток байтов для сохранения в файл, передачи по сети или хранения в БД.

Десериализация — обратный процесс восстановления объекта из байтов.

Для сериализации объект должен реализовать маркерный интерфейс Serializable. Ключевое слово transient исключает поле из сериализации. Нужно быть осторожным с версионированием классов — поле serialVersionUID помогает контролировать совместимость версий.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологий сегодня — 27 Февраля

ℹ️ Кто родился в этот день

Ю́лий Бори́сович Харито́н (14 [27] февраля 1904, Санкт-Петербург — 18 декабря 1996, Саров) — советский и российский физик и физикохимик. Деятель науки и техники — ядерщик. Академик АН СССР и РАН (1953). Один из руководителей советского проекта атомной бомбы.

А́лан Ха́рви Гут (Гус) (англ. Alan Harvey Guth; род. 27 февраля 1947, Нью-Брансуик, округ Мидлсекс, Нью-Джерси, США) — американский физик и космолог, предложивший идею космической инфляции. Его теория требует сложных численных расчётов и стала одним из драйверов развития вычислительной космологии и высокопроизводительных вычислений, где активно используются методы моделирования, статистики и обработки больших данных.


🌐 Знаковые события

1932 — Джеймс Чедвик открыл нейтрон.


#Biography #Birth_Date #Events #27февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Итак, на фоне уже явных сообщений о блокировке телеграмма, раздумываю о переносе канала.

Недавно обнаружил что можно создать канал в max. Нужен ли он?

Хотел спросить вашего мнения:

- нужно ли вообще переносить канал или вы согласны читать его под vpn/санкциями/прочей херней

- если переносить - то как назвать новый канал. Вопрос возник так как мы давно уже не для новичков канал))
Предложите варианты))

- или пора каналу стать прошлым и мне сконцентрироваться на ютубе/рутубе и обучающих роликах, которые дают наибольшую конверсию?

Давайте хорошенько обсудим эти вопросы. Помогите мне принять решение, от него зависит будущее канала.

✌️
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥3😱3
[Совет по Java #010]

Тема: Ключевое слово finally выполняется всегда, даже если в try или catch был return. Но если в finally тоже будет return, он перекроет предыдущий — так не делайте.

Проблема: Блок finally гарантированно выполняется после try (и catch, если было исключение) независимо от того, был ли в них оператор return, break, continue или выброшено исключение.

Это фундаментальное свойство, используемое для очистки ресурсов. Однако если в самом finally поместить return, он перезапишет результат, возвращаемый из try/catch, и "проглотит" любое исключение. Такое поведение крайне неочевидно и приводит к трудноотлаживаемым багам, когда метод возвращает не то значение или не бросает ожидаемое исключение.

Решение: Никогда не используйте return в блоке finally.

Блок finally должен содержать только код очистки (закрытие ресурсов, счетчики, разблокировки) и не должен вмешиваться в поток возврата значения или исключения. Если вам нужно обработать результат или исключение, делайте это в try или catch, а finally оставляйте для побочных эффектов.

import java.io.*;

public class FinallyReturn {

//Антипаттерн: return в finally
public static int badExample() {
try {
System.out.println("Try");
return 42; // Этот return будет проигнорирован
} finally {
System.out.println("Finally");
return 100; // Перекрывает return из try
}
}

//Еще хуже: finally "проглатывает" исключение
public static int badWithException() {
try {
throw new RuntimeException("Ошибка в try");
} finally {
return 200; // Исключение ПРОПАДАЕТ!
}
}

//Правильно: finally без return
public static int goodExample() {
try {
System.out.println("Try");
return 42; // Нормальный выход
} finally {
System.out.println("Finally");
// Здесь только очистка, без return
}
}

//Правильно с исключением
public static int goodWithException() throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
return Integer.parseInt(reader.readLine());
} finally {
if (reader != null) {
try {
reader.close(); // Только закрытие, return нет
} catch (IOException e) {
// Логируем, но не перезаписываем исходное исключение
}
}
}
}

// Демонстрация
public static void main(String[] args) {
System.out.println("badExample(): " + badExample());
// Вывод: Try, Finally, 100 (а не 42!)

System.out.println("badWithException(): " + badWithException());
// Исключение не выбрасывается, выводит 200

System.out.println("goodExample(): " + goodExample());
// Вывод: Try, Finally, 42 (корректно)
}
}


Объяснение: Спецификация Java гарантирует, что блок finally выполняется перед тем, как метод вернет управление.

Если finally содержит return, то значение, вычисленное в try (или выброшенное исключение), отбрасывается, и метод завершается с тем значением, которое указано в finally. Это же касается и исключений — если в finally нет throw, то любое исключение из try/catch считается обработанным. Такое поведение противоречит интуиции и здравому смыслу.

Разработчик использует finally исключительно для освобождения ресурсов (или с 2011 года — try-with-resources), никогда не помещая туда управляющие операторы, влияющие на результат метода.


#Java #советы
👍6
Что выведет код?

public class Task270226 {
public static void main(String[] args) {
System.out.println(test());
}

static String test() {
try {
System.exit(0);
return "try";
} catch (Exception e) {
return "catch";
} finally {
return "finally";
}
}
}


#Tasks
👍2
🔥3👍1
Что такое клоннирование (Cloning)? Как правильно клонировать объекты? 🤓

Ответ:

Клонирование
— создание точной копии объекта.

В Java для этого используется метод clone() класса Object. Чтобы объект можно было клонировать, класс должен реализовать маркерный интерфейс Cloneable (иначе будет CloneNotSupportedException).

Метод clone() делает поверхностное копирование (shallow copy) — копирует примитивы и ссылки, но не сами объекты по ссылкам.

Для глубокого копирования (deep copy) нужно переопределить clone() и вручную клонировать вложенные объекты.

Альтернативы: конструктор копирования, статический фабричный метод, сериализация.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
История технологий сегодня — 28 Февраля

ℹ️ Кто родился в этот день

То́мас Нью́комен (англ. Thomas Newcomen; крещён 28 февраля 1664, Дартмут — 7 августа 1729, Лондон) — английский изобретатель; один из создателей первого теплового (парового) двигателя, известного как паровая машина Ньюкомена.

Леон Н.Купер (англ. Leon Neil Cooper; при рождении — Леон Н. Купчик; 28 февраля 1930, Нью-Йорк, Нью-Йорк — 23 октября 2024, Провиденс) — американский физик, лауреат Нобелевской премии по физике 1972 года (совместно с Джоном Бардиным и Джоном Шриффером) «за создание теории сверхпроводимости, обычно называемой БКШ-теорией». Именем Купера названы электронные пары Купера и BCM теория синаптической пластичности.


🌐 Знаковые события

1956 — в США патентуется сетевой кабель для компьютеров.

1956 — Джей Форрестер получает патент на магнитно‑ядерную память (core memory). Магнитное сердечниковое ОЗУ стало стандартной памятью компьютеров 1950–60‑х и фактически сделало надёжные электронные компьютеры массовыми; без неё не было бы ни ранних мейнфреймов, ни многих космических систем.


#Biography #Birth_Date #Events #28февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
С 21.02 по 27.02
Предыдущий пост(с 14.02 по 20.02)

Воскресный мотивационный пост:
ГЛОБАЛИЗАЦИЯ ЗАКАНЧИВАЕТСЯ

Запись встреч/видео:
7. Введение в распределённую трассировку. OpenTelemetry & Jaeger.

Обучающие статьи:
Раздел 8. Stream API и функциональный стиль

Глава 3: Чистые преобразования и борьба с исключениями
Side Effects — главный враг предсказуемости
(Практика): Чистая агрегация уникальных жанров

Советы по Java:
[Совет по Java #008]
Не ловите Exception или Throwable без крайней необходимости. Ловите максимально конкретные исключения.

[Совет по Java #009]
Используйте Collections.emptyList(), Collections.singletonList() вместо создания новых коллекций, когда нужно вернуть пустой или одноэлементный список.

[Совет по Java #010]
Ключевое слово finally выполняется всегда, даже если в try или catch был return.



Полезные статьи и видео:
JDBC для профи: пулы, batch, транзакции и скрытые риски


Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍2
История технологий сегодня — 29 Февраля/01 марта

ℹ️ Кто родился в этот день

Сеймур Пейперт (Seymour Papert [ˈsimɔɹ ˈpæpɚt]; 29 февраля 1928, Претория, Южная Африка — 31 июля 2016, Блу-Хилл, Мэн, США) — математик, программист, психолог и педагог. Один из основоположников теории искусственного интеллекта, создатель языка Logo (1968), автор конструкционистской теории обучения[6].

Герман Хо́ллерит (англ. Herman Hollerith; 29 февраля 1860 — 17 ноября 1929) — американский инженер и изобретатель. Считается отцом современных автоматических вычислений. Известен как создатель электрической табулирующей системы.


🌐 Знаковые события

1888 — американский инженер Герман Холлерит изобрёл табулятор, первую электрическую счётную машину.

1936 — Нильс Бор предложил теорию составного ядра атома.

1966 — советская автоматическая межпланетная станция Венера-3 впервые в мире достигла поверхности Венеры.


#Biography #Birth_Date #Events #29февраля #01марта
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
Сегодня день тишины))

Я целый день готовился к видео с elk… так и не успел))

Завтра продолжу))
🔥9
История технологий сегодня — 02 марта

ℹ️ Кто родился в этот день

Князь Бори́с Бори́сович Голи́цын (18 февраля [2 марта] 1862, Санкт-Петербург — 4 [17] мая 1916, Петергоф, Петроградская губерния) — русский физик и геофизик из рода Голицыных, один из основоположников сейсмологии, изобретатель первого электромагнитного сейсмографа (1906).


🌐 Знаковые события

1969 — Первый испытательный полёт французского сверхзвукового пассажирского самолёта «Конкорд».


#Biography #Birth_Date #Events #02марта
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
Раздел 8. Stream API и функциональный стиль в Java

Глава 4: Искусство агрегации. Коллекторы и группировки

Коллектор — это рецепт агрегации

Предыдущие главы посвящены преобразованиям потоков: фильтрации, отображению, уплощению. Но поток — эфемерная абстракция. Чтобы извлечь из него ценность, нужно свернуть его в конкретную структуру данных: список, множество, карту, число, строку. Эту свёртку выполняет Collector — центральный интерфейс терминальных операций агрегации.

Collector — не просто утилита для создания List. Это декларативный рецепт, описывающий, как создать контейнер для результатов, как наполнять его элементами, как объединять частичные результаты при параллельном выполнении, и как выполнить финальное преобразование. Понимание его компонентов раскрывает полную мощь Stream API.


Анатомия Collector

Интерфейс Collector<T, A, R> параметризован тремя типами: T — тип элементов входного потока, A — тип промежуточного контейнера аккумуляции, R — тип конечного результата.

Четыре функциональных компонента определяют поведение:

Supplier<A> — фабрика начального состояния. Вызывается один раз для каждого сегмента обработки (при параллельном выполнении — для каждого потока). Создаёт пустой контейнер, готовый к наполнению.
// Для toList(): () -> new ArrayList<T>()
// Для toSet(): () -> new HashSet<T>()
// Для toMap(): () -> new HashMap<K, V>()


BiConsumer<A, T> accumulator — функция накопления. Добавляет элемент потока в контейнер. Вызывается для каждого элемента, не требует возврата значения — мутирует контейнер.
// Для toList(): (list, element) -> list.add(element)
// Для toMap(): (map, element) -> map.put(keyExtractor.apply(element), valueExtractor.apply(element))


BinaryOperator<A> — функция слияния. Объединяет два контейнера в один при параллельном выполнении. Критична для корректности parallelStream: без неё частичные результаты останутся разрозненными.
// Для toList(): (left, right) -> { left.addAll(right); return left; }
// Для toSet(): (left, right) -> { left.addAll(right); return left; }


Function<A, R> finisher — финальное преобразование. Превращает контейнер аккумуляции в конечный результат. Для toList() это тождественное преобразование (list -> list), но для сложных коллекторов может быть нетривиальным: например, collectingAndThen оборачивает результат в unmodifiable коллекцию.
// collectingAndThen(toList(), Collections::unmodifiableList)
// finisher: list -> Collections.unmodifiableList(list)



Характеристики коллектора: оптимизационные подсказки

Третий параметр Collector — Set<Characteristics> — метаданные, влияющие на исполнение:
CONCURRENT — сигнализирует, что один контейнер может безопасно наполняться из нескольких потоков одновременно. При наличии этой характеристики фреймворк создаёт единственный контейнер и вызывает accumulator из множества потоков без предварительного разделения. Пример: Collector.of(ConcurrentHashMap::new, ..., ..., Characteristics.CONCURRENT).

UNORDERED — гарантия, что порядок элементов в результате не важен. Позволяет оптимизировать параллельное выполнение, устраняя накладные расходы на сохранение порядка при слиянии сегментов. toSet() имеет эту характеристику, toList() — нет.

IDENTITY_FINISH — утверждение, что finisher является тождественным преобразованием (A совпадает с R). Фреймворк может пропустить вызов finisher, возвращая контейнер напрямую. Это оптимизация, но также контракт: если объявить эту характеристику и предоставить нетривиальный finisher, получим ClassCastException при попытке привести A к R.


#Java #для_новичков #beginner #stream_api #Collectors #toMap
👍4
Конструирование собственного коллектора

Понимание компонентов позволяет создавать специализированные коллекторы. Рассмотрим задачу: собрать строки в единую строку с ограничением длины, добавляя "[truncated]" при превышении.
public static Collector<String, ?, String> joiningWithLimit(int maxLength) {
class StringBuilderWithFlag {
StringBuilder builder = new StringBuilder();
boolean truncated = false;
}

return Collector.of(
StringBuilderWithFlag::new, // supplier
(container, str) -> { // accumulator
if (container.truncated) return;
if (container.builder.length() + str.length() > maxLength) {
container.builder.append("[truncated]");
container.truncated = true;
} else {
if (container.builder.length() > 0) container.builder.append(", ");
container.builder.append(str);
}
},
(left, right) -> { // combiner
if (left.truncated) return left;
if (right.truncated) {
left.builder.append("[truncated]");
left.truncated = true;
return left;
}
if (left.builder.length() + right.builder.length() > maxLength) {
left.builder.append(", ").append(right.builder).append("[truncated]");
left.truncated = true;
} else {
if (left.builder.length() > 0) left.builder.append(", ");
left.builder.append(right.builder);
}
return left;
},
container -> container.builder.toString(), // finisher
Characteristics.UNORDERED // порядок не важен
);
}

// Использование
String summary = tags.stream()
.collect(joiningWithLimit(100));


Этот коллектор демонстрирует все компоненты: мутабельный контейнер (StringBuilderWithFlag), накопление с условной логикой, сложное слияние при параллелизме, финальное преобразование. Характеристика UNORDERED разрешает оптимизации, но требует корректной реализации combiner для любого порядка элементов.


toMap: мощь и ловушки

Коллектор toMap — один из наиболее полезных и опасных. Он превращает поток пар ключ-значение в Map, но содержит скрытые предположения, нарушение которых ведёт к исключениям.

Ловушка первая: коллизии ключей

При отсутствии функции разрешения коллизий toMap бросает IllegalStateException, если встречает дубликат ключа:
// Опасно: бросит исключение при повторяющемся авторе
Map<String, Book> bookByAuthor = books.stream()
.collect(toMap(Book::author, book -> book)); // Crash на второй книге того же автора


Решение — явная функция разрешения (oldValue, newValue) -> ...:
// Сохраняем первую встреченную книгу автора
Map<String, Book> firstByAuthor = books.stream()
.collect(toMap(
Book::author,
book -> book,
(existing, replacement) -> existing // Игнорируем повторы
));

// Сохраняем последнюю, обновляем
Map<String, Book> lastByAuthor = books.stream()
.collect(toMap(
Book::author,
book -> book,
(oldBook, newBook) -> newBook // Перезаписываем
));

// Агрегируем: список всех книг автора
Map<String, List<Book>> allByAuthor = books.stream()
.collect(toMap(
Book::author,
book -> new ArrayList<>(List.of(book)), // Создаём список из одной книги
(existingList, newList) -> {
existingList.addAll(newList); // Добавляем к существующему
return existingList;
}
));


Последний паттерн настолько распространён, что имеет специализированную реализацию: groupingBy(Book::author, toList()).


#Java #для_новичков #beginner #stream_api #Collectors #toMap
👍3🔥2