История технологий сегодня — 06 марта
ℹ️ Кто родился в этот день
Адам Осборн (англ. Adam Osborne; 6 марта 1939, Бангкок, Таиланд — 18 марта 2003, Кодаиканал[англ.], Индия) — американский предприниматель британского происхождения, издатель книг и создатель программного обеспечения, компьютерный дизайнер, основавший несколько компаний в США и других странах. Создатель первого коммерчески успешного портативного компьютера Osborne 1.
🌐 Знаковые события
1869 — Дмитрий Менделеев представил периодическую систему химических элементов Русскому химическому обществу.
1983 — стартовали продажи Motorola DynaTAC 8000X, первого коммерчески успешного сотового телефона.
1992 — в день рождения великого художника и скульптора Микеланджело впервые активизировался компьютерный вирус Michelangelo, от которого только в Германии пострадали 1500 компьютеров.
#Biography #Birth_Date #Events #06марта
Адам Осборн (англ. Adam Osborne; 6 марта 1939, Бангкок, Таиланд — 18 марта 2003, Кодаиканал[англ.], Индия) — американский предприниматель британского происхождения, издатель книг и создатель программного обеспечения, компьютерный дизайнер, основавший несколько компаний в США и других странах. Создатель первого коммерчески успешного портативного компьютера Osborne 1.
1869 — Дмитрий Менделеев представил периодическую систему химических элементов Русскому химическому обществу.
1983 — стартовали продажи Motorola DynaTAC 8000X, первого коммерчески успешного сотового телефона.
1992 — в день рождения великого художника и скульптора Микеланджело впервые активизировался компьютерный вирус Michelangelo, от которого только в Германии пострадали 1500 компьютеров.
#Biography #Birth_Date #Events #06марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 8. Stream API и функциональный стиль в Java
Глава 4: Искусство агрегации. Коллекторы и группировки
(Практика): Анализ библиотеки в один проход
Подготовка: обновление модели Book
Для этой практики расширьте класс Book:
Задача: три агрегации над библиотекой
Требуемые результаты:
Map<String, Book> — автор → его самая старая книга (минимальный год)
int — общее количество страниц во всех книгах
OptionalDouble — средняя цена книг жанра «Фантастика»
Способ 1: Три отдельных Stream (три прохода)
Простой, читаемый, но неэффективный для больших данных.
Агрегация 1: автор → самая старая книга
Агрегация 2: общее количество страниц
Агрегация 3: средняя цена фантастики
Анализ трёх проходов
Плюсы:
Каждый метод простой и понятный
Легко тестировать изолированно
Легко добавить или убрать агрегацию
Минусы:
Три полных обхода списка — O(3n) время
Три раза загружаются данные в кэш процессора
Для миллиона книг — заметная разница с одним проходом
#Java #для_новичков #beginner #stream_api #практика
Глава 4: Искусство агрегации. Коллекторы и группировки
(Практика): Анализ библиотеки в один проход
Подготовка: обновление модели Book
Для этой практики расширьте класс Book:
public class Book {
private final String title;
private final String author;
private final int year; // год издания
private final int pages; // количество страниц
private final double price; // цена
private final List<String> genres;
// конструктор, геттеры...
public boolean hasGenre(String genre) {
return genres.contains(genre);
}
}Задача: три агрегации над библиотекой
Требуемые результаты:
Map<String, Book> — автор → его самая старая книга (минимальный год)
int — общее количество страниц во всех книгах
OptionalDouble — средняя цена книг жанра «Фантастика»
Способ 1: Три отдельных Stream (три прохода)
Простой, читаемый, но неэффективный для больших данных.
Агрегация 1: автор → самая старая книга
public Map<String, Book> getOldestBookByAuthor() {
return books.stream()
.collect(Collectors.toMap(
Book::getAuthor, // ключ — автор
Function.identity(), // значение — книга
(existing, replacement) -> // разрешение коллизии
existing.getYear() < replacement.getYear()
? existing : replacement
));
}
Что происходит: при встрече второй книги того же автора сравниваем годы, оставляем старую.Агрегация 2: общее количество страниц
public int getTotalPages() {
return books.stream()
.mapToInt(Book::getPages)
.sum();
}
Что происходит: mapToInt создаёт примитивный поток, sum — терминальная редукция.Агрегация 3: средняя цена фантастики
public OptionalDouble getAverageFantasyPrice() {
return books.stream()
.filter(Book::hasGenre, "Фантастика")
.mapToDouble(Book::getPrice)
.average();
}
Что происходит: фильтр оставляет только фантастику, average возвращает OptionalDouble для пустого результата.Анализ трёх проходов
Плюсы:
Каждый метод простой и понятный
Легко тестировать изолированно
Легко добавить или убрать агрегацию
Минусы:
Три полных обхода списка — O(3n) время
Три раза загружаются данные в кэш процессора
Для миллиона книг — заметная разница с одним проходом
#Java #для_новичков #beginner #stream_api #практика
👍4
Способ 2: Один Stream с комбинированным коллектором
Используем Collectors.teeing (Java 12+) или кастомный коллектор для одного прохода.
Подход через teeing (Java 12+)
teeing направляет элементы в два коллектора параллельно, затем объединяет результаты. Для трёх агрегаций применяем вложенно.
Что происходит:
Каждая книга обрабатывается один раз
teeing разветвляет поток: одна копия идёт в Map авторов, другая — в сумматор страниц и усреднитель фантастики
filtering (Java 9+) — коллектор-обёртка, пропускающий элементы по условию
#Java #для_новичков #beginner #stream_api #практика
Используем Collectors.teeing (Java 12+) или кастомный коллектор для одного прохода.
Подход через teeing (Java 12+)
teeing направляет элементы в два коллектора параллельно, затем объединяет результаты. Для трёх агрегаций применяем вложенно.
public LibraryStatistics analyzeInOnePass() {
return books.stream()
.collect(Collectors.teeing(
// Первый коллектор: автор → старая книга
Collectors.toMap(
Book::getAuthor,
Function.identity(),
(a, b) -> a.getYear() < b.getYear() ? a : b
),
// Второй коллектор: teeing для страниц и фантастики
Collectors.teeing(
Collectors.summingInt(Book::getPages),
Collectors.filtering(
b -> b.hasGenre("Фантастика"),
Collectors.averagingDouble(Book::getPrice)
),
(totalPages, avgFantasyPrice) ->
new IntermediateStats(totalPages, avgFantasyPrice)
),
// Объединение результатов
(oldestByAuthor, intermediate) -> new LibraryStatistics(
oldestByAuthor,
intermediate.totalPages,
intermediate.avgFantasyPrice
)
));
}
// Вспомогательные классы-контейнеры
private static class IntermediateStats {
final int totalPages;
final Double avgFantasyPrice; // null если нет фантастики
IntermediateStats(int totalPages, Double avgFantasyPrice) {
this.totalPages = totalPages;
this.avgFantasyPrice = avgFantasyPrice;
}
}
public static class LibraryStatistics {
public final Map<String, Book> oldestByAuthor;
public final int totalPages;
public final OptionalDouble avgFantasyPrice;
// конструктор, преобразование Double → OptionalDouble...
}Что происходит:
Каждая книга обрабатывается один раз
teeing разветвляет поток: одна копия идёт в Map авторов, другая — в сумматор страниц и усреднитель фантастики
filtering (Java 9+) — коллектор-обёртка, пропускающий элементы по условию
#Java #для_новичков #beginner #stream_api #практика
👍5
Подход через кастомный коллектор (Java 8+)
Если teeing недоступен, реализуем Collector вручную:
Практические задания
Задача 1: реализовать оба способа
В проекте «Библиотека» создайте класс LibraryAnalyzer с методами:
analyzeThreePasses() — три отдельных Stream
analyzeOnePassTeeing() — через teeing (если Java 12+)
analyzeOnePassCustom() — через кастомный Collector
Протестируйте на 100, 10 000 и 100 000 книг. Измерьте время System.nanoTime().
Задача 2: добавить четвёртую агрегацию
Добавьте: Map<String, Long> — жанр → количество книг.
Обновите оба подхода. В кастомном коллекторе используйте Map.merge() для подсчёта.
Задача 3: параллельная версия
Модифицируйте кастомный коллектор: добавьте CONCURRENT в характеристики, замените HashMap на ConcurrentHashMap или используйте toConcurrentMap.
Сравните stream() vs parallelStream() для 1 000 000 книг на многоядерной машине.
Задача 4: ленивая статистика (звёздочка)
Реализуйте LazyLibraryStatistics — класс, который не вычисляет агрегации при создании, а при первом обращении к любому полю выполняет один проход и кэширует все результаты.
Используйте Supplier + AtomicReference для thread-safe ленивой инициализации.
#Java #для_новичков #beginner #stream_api #практика
Если teeing недоступен, реализуем Collector вручную:
public LibraryStatistics analyzeWithCustomCollector() {
return books.stream()
.collect(new LibraryStatsCollector());
}
private static class LibraryStatsCollector
implements Collector<Book, StatsAccumulator, LibraryStatistics> {
@Override
public Supplier<StatsAccumulator> supplier() {
return StatsAccumulator::new;
}
@Override
public BiConsumer<StatsAccumulator, Book> accumulator() {
return (acc, book) -> {
// Обновление Map авторов
acc.oldestByAuthor.merge(
book.getAuthor(),
book,
(a, b) -> a.getYear() < b.getYear() ? a : b
);
// Накопление страниц
acc.totalPages += book.getPages();
// Накопление для фантастики
if (book.hasGenre("Фантастика")) {
acc.fantasyCount++;
acc.fantasyPriceSum += book.getPrice();
}
};
}
@Override
public BinaryOperator<StatsAccumulator> combiner() {
return (left, right) -> {
// Слияние для параллельных потоков
left.oldestByAuthor.putAll(right.oldestByAuthor); // с разрешением
left.totalPages += right.totalPages;
left.fantasyCount += right.fantasyCount;
left.fantasyPriceSum += right.fantasyPriceSum;
return left;
};
}
@Override
public Function<StatsAccumulator, LibraryStatistics> finisher() {
return acc -> new LibraryStatistics(
acc.oldestByAuthor,
acc.totalPages,
acc.fantasyCount == 0
? OptionalDouble.empty()
: OptionalDouble.of(acc.fantasyPriceSum / acc.fantasyCount)
);
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet(); // не CONCURRENT из-за HashMap
}
}
private static class StatsAccumulator {
Map<String, Book> oldestByAuthor = new HashMap<>();
int totalPages = 0;
int fantasyCount = 0;
double fantasyPriceSum = 0.0;
}Практические задания
Задача 1: реализовать оба способа
В проекте «Библиотека» создайте класс LibraryAnalyzer с методами:
analyzeThreePasses() — три отдельных Stream
analyzeOnePassTeeing() — через teeing (если Java 12+)
analyzeOnePassCustom() — через кастомный Collector
Протестируйте на 100, 10 000 и 100 000 книг. Измерьте время System.nanoTime().
Задача 2: добавить четвёртую агрегацию
Добавьте: Map<String, Long> — жанр → количество книг.
Обновите оба подхода. В кастомном коллекторе используйте Map.merge() для подсчёта.
Задача 3: параллельная версия
Модифицируйте кастомный коллектор: добавьте CONCURRENT в характеристики, замените HashMap на ConcurrentHashMap или используйте toConcurrentMap.
Сравните stream() vs parallelStream() для 1 000 000 книг на многоядерной машине.
Задача 4: ленивая статистика (звёздочка)
Реализуйте LazyLibraryStatistics — класс, который не вычисляет агрегации при создании, а при первом обращении к любому полю выполняет один проход и кэширует все результаты.
Используйте Supplier + AtomicReference для thread-safe ленивой инициализации.
#Java #для_новичков #beginner #stream_api #практика
👍5
Что выведет код?
#Tasks
import java.util.concurrent.*;
public class Task060326 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.print("GO "));
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
latch.countDown();
latch.await();
barrier.await();
} catch (Exception e) {}
}).start();
}
latch.await();
System.out.print("FINISH ");
}
}
#Tasks
👍2
Варианты ответа:
Anonymous Quiz
40%
GO FINISH
10%
FINISH GO
20%
GO FINISH или FINISH GO (недетерминировано)
30%
Исключение Deadlock
👍2
Что такое CountDownLatch и CyclicBarrier? В чем разница? 🤓
Ответ:
Оба класса из java.util.concurrent используются для синхронизации потоков.
CountDownLatch — это счетчик, который уменьшается вызовом countDown(). Потоки, вызывающие await(), блокируются, пока счетчик не станет равен 0. Используется одноразово, например, когда один поток ждет завершения нескольких других.
CyclicBarrier — это барьер, который собирает потоки. Когда все потоки достигают барьера (вызывают await()), они одновременно освобождаются. Может быть повторно использован (cyclic). Используется, когда потоки ждут друг друга, чтобы начать новый этап работы.
#собеседование
Ответ:
CountDownLatch — это счетчик, который уменьшается вызовом countDown(). Потоки, вызывающие await(), блокируются, пока счетчик не станет равен 0. Используется одноразово, например, когда один поток ждет завершения нескольких других.
CyclicBarrier — это барьер, который собирает потоки. Когда все потоки достигают барьера (вызывают await()), они одновременно освобождаются. Может быть повторно использован (cyclic). Используется, когда потоки ждут друг друга, чтобы начать новый этап работы.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологий сегодня — 07 марта
ℹ️ Кто родился в этот день
Фрэнсис Элизабет «Бетти» Холбертон (7 марта 1917, Филадельфия, Пенсильвания — 8 декабря 2001, Роквилл, Мэриленд) — американский программист. Одна из первых шести программистов ENIAC — первого универсального электронного цифрового компьютера. Автор изобретения точки останова для отладки программ.
🌐 Знаковые события
1876 — Александр Белл получил патент на телефон.
1926 — установлена радиотелефонная связь между Лондоном и Нью-Йорком.
1997 — журнал «Nature» сообщил, что учёные из Рослинского института в Эдинбурге (Шотландия) воспроизвели ягнёнка без участия половых клеток барана. На свет появилась клонированная овца Долли.
#Biography #Birth_Date #Events #07марта
Фрэнсис Элизабет «Бетти» Холбертон (7 марта 1917, Филадельфия, Пенсильвания — 8 декабря 2001, Роквилл, Мэриленд) — американский программист. Одна из первых шести программистов ENIAC — первого универсального электронного цифрового компьютера. Автор изобретения точки останова для отладки программ.
1876 — Александр Белл получил патент на телефон.
1926 — установлена радиотелефонная связь между Лондоном и Нью-Йорком.
1997 — журнал «Nature» сообщил, что учёные из Рослинского института в Эдинбурге (Шотландия) воспроизвели ягнёнка без участия половых клеток барана. На свет появилась клонированная овца Долли.
#Biography #Birth_Date #Events #07марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Как вам первая фаза нашего OrderHub? Нравится?
Можно выбрать много вариантов.
Можно выбрать много вариантов.
Anonymous Poll
80%
Да, все ок, как всегда!
0%
Проработка плохая, настройки проекта не до конца освещены, все работает кое- как
5%
Качество проработки норм, но качество записи или звука так себе.
0%
Плохой сценарий
5%
Много времени занимает болтовня, надо короче, больше кода
10%
Слишком сложно для начинающих
0%
Это не продакшен уровень, а херня
5%
Слишком большие видео некомфортно смотреть
5%
В целом все плохо, прекращай записывать ерунду
🔥5 2🆒1
С 28.02 по 06.03
Предыдущий пост(с 21.02 по 27.02)
Воскресный мотивационный пост:
Не было мотивации
Запись встреч/видео:
8. Логирование и ELK-стек: как расследуют инциденты в production
Обучающие статьи:
Раздел 8. Stream API и функциональный стиль
Глава 4: Искусство агрегации. Коллекторы и группировки
Коллектор — это рецепт агрегации
groupingBy и многоуровневая агрегация
(Практика): Анализ библиотеки в один проход
Советы по Java:
[Совет по Java #011]
Тема: Random — потоконебезопасный и медленный для многопоточки.
[Совет по Java #012]
Тема: При итерации по Map для получения и ключа, и значения используйте entrySet(), а не keySet() с последующим get().
Полезные статьи и видео:
Модель памяти Java процесса
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
Предыдущий пост(с 21.02 по 27.02)
Воскресный мотивационный пост:
Не было мотивации
Запись встреч/видео:
8. Логирование и ELK-стек: как расследуют инциденты в production
Обучающие статьи:
Раздел 8. Stream API и функциональный стиль
Глава 4: Искусство агрегации. Коллекторы и группировки
Коллектор — это рецепт агрегации
groupingBy и многоуровневая агрегация
(Практика): Анализ библиотеки в один проход
Советы по Java:
[Совет по Java #011]
Тема: Random — потоконебезопасный и медленный для многопоточки.
[Совет по Java #012]
Тема: При итерации по Map для получения и ключа, и значения используйте entrySet(), а не keySet() с последующим get().
Полезные статьи и видео:
Модель памяти Java процесса
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍3🔥1
Java for Beginner
Как вам первая фаза нашего OrderHub? Нравится?
Можно выбрать много вариантов.
Можно выбрать много вариантов.
Как всегда все те же голосуют))
Обнял - приподнял❤️
Обнял - приподнял
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологий сегодня — 08 марта
💐 Международный женский день — праздник, который отмечается ежегодно 8 марта в ряде государств и стран мира.
Появился как день солидарности женщин в борьбе за женские права и эмансипацию. В 1945 году устав Организации Объединённых Наций стал первым международным документом, утвердившим принцип равенства мужчин и женщин. С 1977 года Международный женский день отмечается государствами-членами ООН.
Всех присутствующих на канале женщин поздравляю с праздником, желаю всегда оставаться обворожительными и незаменимыми🌹
ℹ️ Кто родился в этот день
Отто Ган (нем. Otto Hahn — Отто Хан; 8 марта 1879, Франкфурт-на-Майне — 28 июля 1968, Гёттинген) — немецкий физик, учёный-новатор в области радиохимии, открывший ядерную изомерию (Уран Z) и расщепление урана. Получил Нобелевскую премию по химии за 1944 год. Гленн Сиборг назвал его «отцом ядерной химии».
Ральф Генри Бер (англ. Ralph Henry Baer; 8 марта 1922, Родальбен, Юго-западный Пфальц — 6 декабря 2014, Манчестер, Нью-Гэмпшир) — американский изобретатель, разработчик видеоигр, инженер, предприниматель, получивший прозвище «Отец видеоигр» за свой огромный вклад в развитие этой индустрии, особенно в первые годы существования компьютерных игр как таковых. В 1956 году основал собственную компанию. В ней работали до 500 инженеров, которые занимались преимущественно военными заказами. Именно эта работа подала Беру идею, которая в итоге привела к созданию первой в мире игровой приставки.
Майкл Стерн Харт (англ. Michael Stern Hart; 8 марта 1947 — 6 сентября 2011) — американский писатель, изобретатель электронных книг и основатель проекта «Гутенберг», который сделал электронные книги свободно доступными через Интернет. Большинство первых материалов он напечатал и разместил лично. В 1971 году Майкл Харт получил неограниченный доступ ко времени крупного компьютера Xerox Sigma V от операторов в университете штата Иллинойс. Пытаясь достойно применить этот ресурс, он создал первую электронную книгу Декларация независимости США, когда впечатал её текст в компьютер. Так путём создания электронных копий большего количества книг получил начало Проект «Гутенберг». Избегал расходов на врачей, предпочитая домашние средства. Собрал много компьютеров и стереосистем, другой аппаратуры, часто из вышедшего из употребления оборудования, жертвуя собственной роскошью ради борьбы с безграмотностью и сохранения доступа общественности к информационным ресурсам. Вёл жизнь, близкую к бедности, «питаясь, в основном, бобовыми консервами».
🌐 Знаковые события
1950 — СССР объявил о наличии атомной бомбы.
1979 — компания Philips представляет компакт-диск.
1979 — изображения, сделанные Вояджером-I, доказали существование вулканизма на Ио, спутнике Юпитера.
#Biography #Birth_Date #Events #08марта
Появился как день солидарности женщин в борьбе за женские права и эмансипацию. В 1945 году устав Организации Объединённых Наций стал первым международным документом, утвердившим принцип равенства мужчин и женщин. С 1977 года Международный женский день отмечается государствами-членами ООН.
Всех присутствующих на канале женщин поздравляю с праздником, желаю всегда оставаться обворожительными и незаменимыми
Отто Ган (нем. Otto Hahn — Отто Хан; 8 марта 1879, Франкфурт-на-Майне — 28 июля 1968, Гёттинген) — немецкий физик, учёный-новатор в области радиохимии, открывший ядерную изомерию (Уран Z) и расщепление урана. Получил Нобелевскую премию по химии за 1944 год. Гленн Сиборг назвал его «отцом ядерной химии».
Ральф Генри Бер (англ. Ralph Henry Baer; 8 марта 1922, Родальбен, Юго-западный Пфальц — 6 декабря 2014, Манчестер, Нью-Гэмпшир) — американский изобретатель, разработчик видеоигр, инженер, предприниматель, получивший прозвище «Отец видеоигр» за свой огромный вклад в развитие этой индустрии, особенно в первые годы существования компьютерных игр как таковых. В 1956 году основал собственную компанию. В ней работали до 500 инженеров, которые занимались преимущественно военными заказами. Именно эта работа подала Беру идею, которая в итоге привела к созданию первой в мире игровой приставки.
Майкл Стерн Харт (англ. Michael Stern Hart; 8 марта 1947 — 6 сентября 2011) — американский писатель, изобретатель электронных книг и основатель проекта «Гутенберг», который сделал электронные книги свободно доступными через Интернет. Большинство первых материалов он напечатал и разместил лично. В 1971 году Майкл Харт получил неограниченный доступ ко времени крупного компьютера Xerox Sigma V от операторов в университете штата Иллинойс. Пытаясь достойно применить этот ресурс, он создал первую электронную книгу Декларация независимости США, когда впечатал её текст в компьютер. Так путём создания электронных копий большего количества книг получил начало Проект «Гутенберг». Избегал расходов на врачей, предпочитая домашние средства. Собрал много компьютеров и стереосистем, другой аппаратуры, часто из вышедшего из употребления оборудования, жертвуя собственной роскошью ради борьбы с безграмотностью и сохранения доступа общественности к информационным ресурсам. Вёл жизнь, близкую к бедности, «питаясь, в основном, бобовыми консервами».
1950 — СССР объявил о наличии атомной бомбы.
1979 — компания Philips представляет компакт-диск.
1979 — изображения, сделанные Вояджером-I, доказали существование вулканизма на Ио, спутнике Юпитера.
#Biography #Birth_Date #Events #08марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологий сегодня — 09 марта
ℹ️ Кто родился в этот день
Ю́рий Алексе́евич Гага́рин (9 марта 1934, Клушино, Гжатский (ныне Гагаринский) район, Западная область (ныне — Смоленская область) — 27 марта 1968, возле села Новосёлово, Киржачский район, Владимирская область) — советский космонавт и военный лётчик, первый человек, совершивший космический полёт. Герой Советского Союза, кавалер высших знаков отличия ряда государств, почётный гражданин многих российских и зарубежных городов.
Го́вард Ха́тауэй Э́йкен (англ. Howard Hathaway Aiken; 9 марта 1900, Хобокен, штат Нью-Джерси, США — 14 марта 1973, Сент-Луис, штат Миссури, США) — американский пионер компьютеростроения. В должности инженера IBM руководил работами по созданию первого американского компьютера «Марк I».
Эндрю Джеймс Витерби (англ. Andrew James Viterbi, имя при рождении Андреа Джакомо Витерби (итал. Andrea Giacomo Viterbi); род. 9 марта 1935 года, Бергамо, Королевство Италия) — американский инженер и бизнесмен, сооснователь Qualcomm и разработчик алгоритма Витерби.
🌐 Знаковые события
1937 — Московский телецентр на Шаболовке осуществил первую в СССР опытную передачу электронного телевидения в эфир.
1948 — на циклотроне в Калифорнии удаётся получить мезоны.
#Biography #Birth_Date #Events #09марта
Ю́рий Алексе́евич Гага́рин (9 марта 1934, Клушино, Гжатский (ныне Гагаринский) район, Западная область (ныне — Смоленская область) — 27 марта 1968, возле села Новосёлово, Киржачский район, Владимирская область) — советский космонавт и военный лётчик, первый человек, совершивший космический полёт. Герой Советского Союза, кавалер высших знаков отличия ряда государств, почётный гражданин многих российских и зарубежных городов.
Го́вард Ха́тауэй Э́йкен (англ. Howard Hathaway Aiken; 9 марта 1900, Хобокен, штат Нью-Джерси, США — 14 марта 1973, Сент-Луис, штат Миссури, США) — американский пионер компьютеростроения. В должности инженера IBM руководил работами по созданию первого американского компьютера «Марк I».
Эндрю Джеймс Витерби (англ. Andrew James Viterbi, имя при рождении Андреа Джакомо Витерби (итал. Andrea Giacomo Viterbi); род. 9 марта 1935 года, Бергамо, Королевство Италия) — американский инженер и бизнесмен, сооснователь Qualcomm и разработчик алгоритма Витерби.
1937 — Московский телецентр на Шаболовке осуществил первую в СССР опытную передачу электронного телевидения в эфир.
1948 — на циклотроне в Калифорнии удаётся получить мезоны.
#Biography #Birth_Date #Events #09марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #013]
Тема: Аннотация @Transactional не работает на private и protected методах. Spring использует AOP-прокси, которые не могут переопределить не-public методы.
Проблема: Многие разработчики ошибочно полагают, что достаточно поставить @Transactional над любым методом, и Spring магическим образом обеспечит управление транзакциями.
Однако механизм работы Spring Transactions основан на AOP-прокси (динамических прокси JDK или CGLIB-прокси). Прокси — это подкласс или реализация интерфейса целевого класса, который переопределяет публичные методы для добавления сквозной функциональности (открытие/закрытие транзакции).
Если метод помечен как private или protected, прокси не может его переопределить, и аннотация игнорируется. Метод выполняется без транзакционного контекста, что приводит к трудноотлавливаемым багам: данные сохраняются неполностью, отсутствует откат при исключениях, соединения не закрываются.
Решение: Всегда используйте @Transactional только на public методах.
Транзакционность должна применяться на уровне сервиса (фасада), а не на внутренних вспомогательных методах. Если вам нужно вызвать транзакционный метод из того же класса, используйте self-injection (внедрение ссылки на самого себя через @Autowired/@jakarta.inject.Inject) или выделите транзакционную логику в отдельный @Service-компонент. В Spring 4.3+ можно использовать @Autowired для внедрения прокси самого себя.
Объяснение: Spring создает прокси вокруг целевого бина.
При вызове метода через прокси сначала выполняется транзакционный перехватчик, который открывает транзакцию, затем вызывается оригинальный метод, после чего транзакция коммитится или откатывается. При вызове private метода прокси не участвует — вызов идет напрямую к целевому объекту (this). Self-injection решает эту проблему, заставляя обращаться к самому себе через прокси.
Важно понимать, что для работы @Transactional нужен публичный метод, управление транзакциями на уровне контейнера и правильная конфигурация прокси (по умолчанию — JDK dynamic proxy, требующий интерфейса, или CGLIB, работающий с классами).
#Java #советы
Тема: Аннотация @Transactional не работает на private и protected методах. Spring использует AOP-прокси, которые не могут переопределить не-public методы.
Проблема: Многие разработчики ошибочно полагают, что достаточно поставить @Transactional над любым методом, и Spring магическим образом обеспечит управление транзакциями.
Однако механизм работы Spring Transactions основан на AOP-прокси (динамических прокси JDK или CGLIB-прокси). Прокси — это подкласс или реализация интерфейса целевого класса, который переопределяет публичные методы для добавления сквозной функциональности (открытие/закрытие транзакции).
Если метод помечен как private или protected, прокси не может его переопределить, и аннотация игнорируется. Метод выполняется без транзакционного контекста, что приводит к трудноотлавливаемым багам: данные сохраняются неполностью, отсутствует откат при исключениях, соединения не закрываются.
Решение: Всегда используйте @Transactional только на public методах.
Транзакционность должна применяться на уровне сервиса (фасада), а не на внутренних вспомогательных методах. Если вам нужно вызвать транзакционный метод из того же класса, используйте self-injection (внедрение ссылки на самого себя через @Autowired/@jakarta.inject.Inject) или выделите транзакционную логику в отдельный @Service-компонент. В Spring 4.3+ можно использовать @Autowired для внедрения прокси самого себя.
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserService selfProxy; // Self-injection для вызова из того же класса
//Антипаттерн: @Transactional на private методе
@Transactional
private void badPrivateMethod(User user) {
// Транзакции НЕ БУДЕТ! Метод не переопределяется прокси
userRepository.save(user);
if (user.getEmail() == null) {
throw new RuntimeException("Ошибка — отката не будет!");
}
}
//Правильно: public метод
@Transactional
public void goodPublicMethod(User user) {
userRepository.save(user);
if (user.getEmail() == null) {
throw new RuntimeException("Ошибка — транзакция откатится");
}
}
// Проблема внутреннего вызова
public void callInternal() {
User user = new User("John");
//Транзакции не будет — вызов напрямую, не через прокси
goodPublicMethod(user); // ФАКТИЧЕСКИ: this.goodPublicMethod()
}
//Решение: вызов через self-proxy
public void callCorrectly() {
User user = new User("John");
selfProxy.goodPublicMethod(user); // Вызов через прокси — транзакция работает
}
}
// Альтернативное решение: вынос логики в отдельный компонент
@Service
class InternalUserService {
@Transactional
public void doInTransaction(User user) {
// транзакционная логика
}
}
@Service
class MainUserService {
@Autowired
private InternalUserService internalService;
public void call() {
internalService.doInTransaction(new User("John")); // Транзакция работает
}
}
Объяснение: Spring создает прокси вокруг целевого бина.
При вызове метода через прокси сначала выполняется транзакционный перехватчик, который открывает транзакцию, затем вызывается оригинальный метод, после чего транзакция коммитится или откатывается. При вызове private метода прокси не участвует — вызов идет напрямую к целевому объекту (this). Self-injection решает эту проблему, заставляя обращаться к самому себе через прокси.
Важно понимать, что для работы @Transactional нужен публичный метод, управление транзакциями на уровне контейнера и правильная конфигурация прокси (по умолчанию — JDK dynamic proxy, требующий интерфейса, или CGLIB, работающий с классами).
#Java #советы
👍5
Что выведет код?
#Tasks
@SpringBootApplication
public class TransactionalTricky implements CommandLineRunner {
@Autowired
private UserService userService;
public static void main(String[] args) {
SpringApplication.run(TransactionalTricky.class, args);
}
@Override
public void run(String... args) {
userService.saveUser();
System.out.println(userService.getCounter());
}
@Service
static class UserService {
private int counter = 0;
@Transactional
public void saveUser() {
privateIncrement();
}
@Transactional
private void privateIncrement() {
counter++;
}
public int getCounter() {
return counter;
}
}
}
#Tasks
👍3
👍4
Что такое ConcurrentHashMap и чем он лучше Hashtable? 🤓
Ответ:
ConcurrentHashMap из пакета java.util.concurrent — это потокобезопасная реализация Map, оптимизированная для многопоточной среды.
В отличие от Hashtable, которая синхронизирует все методы (блокируя всю таблицу), ConcurrentHashMap использует более тонкую блокировку.
В Java 7 — сегментирование, в Java 8+ блокируются отдельные элементы корзины (или используется Compare-And-Swap). Это позволяет параллельно выполнять чтение и запись в разные сегменты/корзины. Он также не выбрасывает ConcurrentModificationException при итерации.
#собеседование
Ответ:
В отличие от Hashtable, которая синхронизирует все методы (блокируя всю таблицу), ConcurrentHashMap использует более тонкую блокировку.
В Java 7 — сегментирование, в Java 8+ блокируются отдельные элементы корзины (или используется Compare-And-Swap). Это позволяет параллельно выполнять чтение и запись в разные сегменты/корзины. Он также не выбрасывает ConcurrentModificationException при итерации.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологий сегодня — 10 марта
ℹ️ Кто родился в этот день
Евге́ний Ла́заревич Роша́л (род. 10 марта 1972, Челябинск) — российский программист, автор файлового менеджера FAR Manager, формата сжатия RAR, архиваторов RAR и WinRAR.
🌐 Знаковые события
1977 — несколько групп астрономов США, Австралии, Индии, ЮАР открыли кольца Урана.
1982 — редкий парад планет — все девять планет (Меркурий, Венера, Земля, Марс, Юпитер, Сатурн, Уран, Нептун и Плутон) собрались по одну сторону от Солнца в секторе с углом 95 градусов (то есть, максимальная разность гелиоцентрических эклиптических долгот планет составила 95 градусов).
2006 — автоматическая межпланетная станция Mars Reconnaissance Orbiter достигла Марса.
#Biography #Birth_Date #Events #10марта
Евге́ний Ла́заревич Роша́л (род. 10 марта 1972, Челябинск) — российский программист, автор файлового менеджера FAR Manager, формата сжатия RAR, архиваторов RAR и WinRAR.
1977 — несколько групп астрономов США, Австралии, Индии, ЮАР открыли кольца Урана.
1982 — редкий парад планет — все девять планет (Меркурий, Венера, Земля, Марс, Юпитер, Сатурн, Уран, Нептун и Плутон) собрались по одну сторону от Солнца в секторе с углом 95 градусов (то есть, максимальная разность гелиоцентрических эклиптических долгот планет составила 95 градусов).
2006 — автоматическая межпланетная станция Mars Reconnaissance Orbiter достигла Марса.
#Biography #Birth_Date #Events #10марта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 8. Stream API и функциональный стиль в Java
Глава 5: Контракты. Почему equals, hashCode и иммутабельность — закон
equals/hashCode в операциях distinct, groupingBy, toSet
Stream API не существует в вакууме. Он опирается на фундаментальные контракты Java-объектов, установленные классом Object: equals для логического сравнения, hashCode для хеширования, compareTo для упорядочивания.
Нарушение этих контрактов не всегда приводит к немедленным ошибкам компиляции или очевидным исключениям. Вместо этого порождаются "мистические" баги: данные исчезают, дублируются, группируются неправильно — и всё это проявляется недетерминированно, часто только под нагрузкой или при смене окружения.
Рассмотрим класс Book, разработанный без должного внимания к контрактам:
По умолчанию equals и hashCode наследуются от Object, реализуя идентичность по ссылке: два объекта равны только если это один и тот же экземпляр в памяти. Для доменной модели, где логическая эквивалентность определяется содержимым, это поведение некорректно.
groupingBy и распределение по бакетам
Коллектор groupingBy использует HashMap для организации групп. При добавлении элемента вычисляется хеш-код ключа, определяющий бакет (сегмент внутреннего массива HashMap). Если в бакете уже есть элементы, выполняется проверка equals для разрешения коллизий.
Проблема возникает при использовании ключа без корректного hashCode:
Без переопределённого hashCode book1 и book2 имеют разные хеш-коды (по умолчанию System.identityHashCode), вычисляемые из адресов памяти. Они попадают в разные бакеты HashMap. Внутри каждого бакета equals возвращает false, потому что сравнение по ссылке даёт отрицательный результат. Результат: две записи в Map вместо одной, счётчики показывают 2 для каждой "уникальной" книги.
Баг проявляется недетерминированно. При небольшом числе элементов HashMap может поместить оба объекта в один бакет случайно (из-за пересечения хешей по модулю размера таблицы), и equals обнаружит их различность, но коллизия разрешится корректно — повезло. При росте данных таблица расширяется, бакеты становятся уже, вероятность коллизии падает, баг становится стабильным. При смене JVM (разная версия, разная реализация Object.hashCode) хеш-коды меняются, и поведение меняется без изменения кода.
distinct: фильтрация дубликатов без гарантий
Операция distinct() использует LinkedHashSet для отслеживания уникальных элементов. Она полагается на тот же контракт equals/hashCode:
Без корректных методов distinct не удаляет логические дубликаты, только ссылочные. Если book1 и book2 — разные объекты с одинаковым содержимым, оба попадут в результат. Память растёт, кэширование не работает.
Особенно опасно это в распределённых системах: объект десериализуется в разных узлах, создавая разные экземпляры в памяти. Без equals/hashCode по значению они воспринимаются как разные сущности.
toSet и потеря элементов
Collectors.toSet() возвращает HashSet, основанный на хеш-таблице. Некорректный hashCode ведёт к дублированию:
HashSet добавляет элемент, если в соответствующем бакете нет равного (по equals). Разные хеш-коды → разные бакеты → оба элемента добавлены. Множество перестаёт быть множеством.
#Java #для_новичков #beginner #stream_api #equals_hashCode
Глава 5: Контракты. Почему equals, hashCode и иммутабельность — закон
equals/hashCode в операциях distinct, groupingBy, toSet
Stream API не существует в вакууме. Он опирается на фундаментальные контракты Java-объектов, установленные классом Object: equals для логического сравнения, hashCode для хеширования, compareTo для упорядочивания.
Нарушение этих контрактов не всегда приводит к немедленным ошибкам компиляции или очевидным исключениям. Вместо этого порождаются "мистические" баги: данные исчезают, дублируются, группируются неправильно — и всё это проявляется недетерминированно, часто только под нагрузкой или при смене окружения.
Рассмотрим класс Book, разработанный без должного внимания к контрактам:
public class Book {
private final String title;
private final String author;
private final int year;
// Конструктор, геттеры
// equals и hashCode НЕ переопределены!
}По умолчанию equals и hashCode наследуются от Object, реализуя идентичность по ссылке: два объекта равны только если это один и тот же экземпляр в памяти. Для доменной модели, где логическая эквивалентность определяется содержимым, это поведение некорректно.
groupingBy и распределение по бакетам
Коллектор groupingBy использует HashMap для организации групп. При добавлении элемента вычисляется хеш-код ключа, определяющий бакет (сегмент внутреннего массива HashMap). Если в бакете уже есть элементы, выполняется проверка equals для разрешения коллизий.
Проблема возникает при использовании ключа без корректного hashCode:
// Две логически идентичные книги
Book book1 = new Book("1984", "George Orwell", 1949);
Book book2 = new Book("1984", "George Orwell", 1949);
// Создаём поток с дублированием
List<Book> library = Arrays.asList(book1, book1, book2, book2);
// Группировка по книге как ключу
Map<Book, Long> countByBook = library.stream()
.collect(groupingBy(Function.identity(), counting()));
System.out.println(countByBook.size()); // Ожидаем 1, получаем...?
Без переопределённого hashCode book1 и book2 имеют разные хеш-коды (по умолчанию System.identityHashCode), вычисляемые из адресов памяти. Они попадают в разные бакеты HashMap. Внутри каждого бакета equals возвращает false, потому что сравнение по ссылке даёт отрицательный результат. Результат: две записи в Map вместо одной, счётчики показывают 2 для каждой "уникальной" книги.
Баг проявляется недетерминированно. При небольшом числе элементов HashMap может поместить оба объекта в один бакет случайно (из-за пересечения хешей по модулю размера таблицы), и equals обнаружит их различность, но коллизия разрешится корректно — повезло. При росте данных таблица расширяется, бакеты становятся уже, вероятность коллизии падает, баг становится стабильным. При смене JVM (разная версия, разная реализация Object.hashCode) хеш-коды меняются, и поведение меняется без изменения кода.
distinct: фильтрация дубликатов без гарантий
Операция distinct() использует LinkedHashSet для отслеживания уникальных элементов. Она полагается на тот же контракт equals/hashCode:
List<Book> uniqueBooks = library.stream()
.distinct()
.collect(toList());
Без корректных методов distinct не удаляет логические дубликаты, только ссылочные. Если book1 и book2 — разные объекты с одинаковым содержимым, оба попадут в результат. Память растёт, кэширование не работает.
Особенно опасно это в распределённых системах: объект десериализуется в разных узлах, создавая разные экземпляры в памяти. Без equals/hashCode по значению они воспринимаются как разные сущности.
toSet и потеря элементов
Collectors.toSet() возвращает HashSet, основанный на хеш-таблице. Некорректный hashCode ведёт к дублированию:
Set<Book> bookSet = library.stream()
.collect(toSet());
System.out.println(bookSet.size()); // Ожидаем 1, получаем 2
HashSet добавляет элемент, если в соответствующем бакете нет равного (по equals). Разные хеш-коды → разные бакеты → оба элемента добавлены. Множество перестаёт быть множеством.
#Java #для_новичков #beginner #stream_api #equals_hashCode
👍5
Иммутабельность ключей: бомба замедленного действия
Даже при корректных equals/hashCode опасность подстерегает в изменяемых ключах. Рассмотрим сценарий с составным ключом-автором:
HashMap хранит ключ в бакете, соответствующем хеш-коду на момент вставки. При изменении полей name или country hashCode объекта меняется, но позиция в таблице — нет. get(key) вычисляет новый хеш, ищет в другом бакете, не находит совпадения, возвращает null. Данные не потеряны физически (объект Author и список Book существуют в памяти), но недоступны через Map.
Это "утечка" памяти логического характера: структура данных разрастается, но поиск перестаёт работать. Баг проявляется далеко от места мутации, диагностика требует глубокого понимания работы хеш-таблиц.
Правильная реализация контрактов
Для Book корректные equals и hashCode должны учитывать все значимые поля:
Ключевые требования к контракту:
Рефлексивность: x.equals(x) всегда true
Симметричность: x.equals(y) тогда и только тогда, когда y.equals(x)
Транзитивность: если x.equals(y) и y.equals(z), то x.equals(z)
Консистентность: многократные вызовы дают один результат (требует иммутабельности)
Согласованность с hashCode: равные объекты имеют равные хеш-коды
Последнее требование критично для HashMap/HashSet. Нарушение (equals true, но hashCode разный) делает коллекции неработоспособными.
Рекорды как решение
Java Records (14+, стабильно в 16+) автоматически генерируют корректные equals, hashCode, toString по всем компонентам, гарантируя иммутабельность:
Использование records для ключей groupingBy, элементов distinct, элементов toSet устраняет класс ошибок, связанных с контрактами. Единственное ограничение: компоненты records также должны быть иммутабельными и с корректными контрактами. Если Author в Book — изменяемый класс, проблема сохраняется.
Диагностика в production
При подозрении на нарушение контрактов:
Логирование хеш-кодов: вывести System.identityHashCode(obj) и obj.hashCode() для "одинаковых" объектов. Различие указывает на проблему.
Проверка equals: убедиться, что obj1.equals(obj2) возвращает true для логически эквивалентных объектов.
Анализ мутаций: установить точки останова на сеттеры ключей, используемых в Map.
Использование LinkedHashMap/LinkedHashSet: сохраняют порядок вставки, облегчая отладку.
#Java #для_новичков #beginner #stream_api #equals_hashCode
Даже при корректных equals/hashCode опасность подстерегает в изменяемых ключах. Рассмотрим сценарий с составным ключом-автором:
public class Author {
private String name; // Не final! Сеттер есть!
private String country;
// equals и hashCode по всем полям
@Override public int hashCode() {
return Objects.hash(name, country);
}
}
// Группировка книг по автору
Map<Author, List<Book>> byAuthor = library.stream()
.collect(groupingBy(Book::author));
// Позже в коде...
Author key = byAuthor.keySet().iterator().next();
key.setName("Modified Name"); // Мутация ключа!
// Попытка доступа
List<Book> books = byAuthor.get(key); // null?!HashMap хранит ключ в бакете, соответствующем хеш-коду на момент вставки. При изменении полей name или country hashCode объекта меняется, но позиция в таблице — нет. get(key) вычисляет новый хеш, ищет в другом бакете, не находит совпадения, возвращает null. Данные не потеряны физически (объект Author и список Book существуют в памяти), но недоступны через Map.
Это "утечка" памяти логического характера: структура данных разрастается, но поиск перестаёт работать. Баг проявляется далеко от места мутации, диагностика требует глубокого понимания работы хеш-таблиц.
Правильная реализация контрактов
Для Book корректные equals и hashCode должны учитывать все значимые поля:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return year == book.year &&
Objects.equals(title, book.title) &&
Objects.equals(author, book.author);
}
@Override
public int hashCode() {
return Objects.hash(title, author, year);
}
Ключевые требования к контракту:
Рефлексивность: x.equals(x) всегда true
Симметричность: x.equals(y) тогда и только тогда, когда y.equals(x)
Транзитивность: если x.equals(y) и y.equals(z), то x.equals(z)
Консистентность: многократные вызовы дают один результат (требует иммутабельности)
Согласованность с hashCode: равные объекты имеют равные хеш-коды
Последнее требование критично для HashMap/HashSet. Нарушение (equals true, но hashCode разный) делает коллекции неработоспособными.
Рекорды как решение
Java Records (14+, стабильно в 16+) автоматически генерируют корректные equals, hashCode, toString по всем компонентам, гарантируя иммутабельность:
public record Book(String title, String author, int year) {}
// Автоматически: final поля, конструктор, геттеры, equals, hashCode, toStringИспользование records для ключей groupingBy, элементов distinct, элементов toSet устраняет класс ошибок, связанных с контрактами. Единственное ограничение: компоненты records также должны быть иммутабельными и с корректными контрактами. Если Author в Book — изменяемый класс, проблема сохраняется.
Диагностика в production
При подозрении на нарушение контрактов:
Логирование хеш-кодов: вывести System.identityHashCode(obj) и obj.hashCode() для "одинаковых" объектов. Различие указывает на проблему.
Проверка equals: убедиться, что obj1.equals(obj2) возвращает true для логически эквивалентных объектов.
Анализ мутаций: установить точки останова на сеттеры ключей, используемых в Map.
Использование LinkedHashMap/LinkedHashSet: сохраняют порядок вставки, облегчая отладку.
#Java #для_новичков #beginner #stream_api #equals_hashCode
👍7
Что выведет код?
#Tasks
import java.util.*;
import java.util.stream.Collectors;
public class Task100326 {
public static void main(String[] args) {
List<Item100326> items = Arrays.asList(
new Item100326(1, "A"),
new Item100326(1, "B"),
new Item100326(2, "C"),
new Item100326(2, "D")
);
Set<Item100326> set = new LinkedHashSet<>(items);
Map<Integer, List<Item100326>> grouped = items.stream()
.collect(Collectors.groupingBy(Item100326::getId));
System.out.print(set.size() + " ");
System.out.print(grouped.get(1).size() + " ");
System.out.print(grouped.get(2).get(0).getName());
}
static class Item100326 {
private int id;
private String name;
Item100326(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { return id; }
public String getName() { return name; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item100326 item = (Item100326) o;
return id == item.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
}
#Tasks
👍3