Java: fill the gaps
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
显示更多📈 Telegram 频道 Java: fill the gaps 的分析概览
频道 Java: fill the gaps (@java_fillthegaps) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 12 548 名订阅者,在 技术与应用 类别中位列第 10 113,并在 俄罗斯 地区排名第 52 819 位。
📊 受众指标与增长动态
自 невідомо 创建以来,项目保持高速增长,吸引了 12 548 名订阅者。
根据 08 六月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 -43,过去 24 小时变化为 -3,整体触达仍然可观。
- 认证状态: 未认证
- 互动率 (ER): 平均受众互动率为 34.73%。内容发布后 24 小时内通常能获得 N/A% 的反应,占订阅者总量。
- 帖子覆盖: 每篇帖子平均可获得 0 次浏览,首日通常累积 0 次浏览。
- 互动与反馈: 受众积极参与,单帖平均反应数为 0。
- 主题关注点: 内容集中在 redis, hashmap, linkedhashmap, индекс, фича 等核心主题上。
📝 描述与内容策略
作者将该频道定位为表达主观观点的平台:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
凭借高频更新(最新数据采集于 09 六月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
peek() был единственным способом посмотреть элементы внутри стрима:
list.stream().filter(..)
.peek(e->println("filt:"+e))
.map(..)
.peek(e->println("map:"+e))
...
Недавно в Intellij IDEA вышел потрясающий апдейт, который выводит дебаг стримов на новый уровень:Stream.iterate(T, BinaryOperator)
🔸 Задать первый элемент, правило вывода последующего и условие остановки: Stream.iterate(T, Predicate, BinaryOperator) (java 9)
🔸 Генерировать независимые друг от друга элементы: Stream.generate(Supplier)
🔸 Из диапазона с включением граничных значений: IntStream.range(..)
🔸 Диапазон без граничных значений: IntStream.rangeClosed(..)
🔸 Из строк: BufferedReader.lines()
🔸 Из символов строки: CharSequence.chars()
Названия большинства методов преобразований говорят сами за себя: filter, distinct, limit, sorted. Есть менее понятные:
🔹 map: применить функцию к каждому элементу.
🔹 flatmap: положить элементы из "списка списков" в единый стрим. Также с его помощью можно уменьшить размерность массива.
🔹 takeWhile(Predicate): брать элементы из стрима, пока выполняется условие. Метод доступен c java 9.
🔹 dropWhile(Predicate): пропускать элементы, пока условие не нарушится. Метод доступен c java 9.
🔹 peek(Consumer): сделать что-то с каждым элементом стрима, не меняя исходный стрим. Часто используется при дебаге, например, вывести в консоль текущие элементы.
Вычисления выполняются не сразу, а только когда вызывается конечная операция. Конечная операция описывает желаемый результат. Результатом может быть:
◾️ Структура данных: toArray, collect.
◾️ Результат поиска: anyMatch, allMatch, nonMatch, findFirst, findAny.
◾️ Результат агрегации: min, max, count, reduce.
◾️ Сайд-эффект: forEach, forEachOrdered.
Стрим - не структура хранения данных. Он обходит источник данных (или сам его генерирует) и сохраняет промежуточные результаты. По этой причине нельзя копировать стримы и исполнять их несколько раз - пришлось бы контролировать этапы исполнения, консистентность данных и добавить синхронизацию для работы в многопоточной среде. При попытке переиспользования стрима выбрасывается IllegalStateException.
✅ Исходные данные не меняются.
✅ Стрим нельзя переиспользовать.
✅ Пока не вызвана конечная операция, стрим можно менять как угодно.
Пример из опроса выше вернёт 1.
list.remove(0) удаляет элемент с индексом 0, поэтому на момент старта вычислений stream.count() в источнике данных остаётся всего один элемент.
Если Вас раздражает, что метод list.remove принимает не сам элемент, а индекс, то некоторые методы Stream API Вас тоже разочаруют. Например, такой код компилируется:
Stream.of(-1, 0, 1).max(Math::max).get();Но в результате получается -1, потому что входной параметр метода
max - Comparator. Math.max по сигнатуре на него похож, поэтому получается неверный результат😒List list=new ArrayList();
list.add(1);
list.add(2);
Stream stream=list.stream();
list.remove(0);
println(stream.count());public void add(List list, Integer value)
{ list.add(value); }
✅ Минимальный расход памяти.
❌ Для запуска в многопоточной среде нужна синхронизация.
❌ Если метод прервался, то нужно выяснять, что он успел изменить, а что нет.
Подход ФП: создаём новый список на основе исходного и добавляем туда элемент.
public List add(List list, Integer value {
List newList=new List();
newList.addAll(list);
newList.add(value);
return list;
}
✅ Можно использовать в многопоточной среде.
✅ Если что-то пошло не так, просто ещё раз вызываем метод.
❌ Большой расход памяти.
3️⃣ Декларативный стиль написания кода.
Обход коллекции в цикле — это императивный подход. Для каждого элемента задаётся чёткая последовательность действий.
Stream API — декларативный. Благодаря большому количеству встроенных функций можно просто описать то, что нужно: отфильтровать, сгруппировать, просуммировать.
Лучшая читаемость - не единственное преимущество такого подхода. Чистые функции и неизменяемые данные сами по себе тоже не сделают программу эффективной. Но их использование в Stream API дают компилятору и JVM большие возможности для оптимизаций. Cамая мощная из них — опция parallel(), она автоматически распределяет данные по параллельным подзадачам.
Начиная с 8 версии java можно применять и другие идеи ФП — функции высших порядков, currying, pattern matching и т.д. Некоторые подходы были доступны и раньше, например, использовать хвостовую рекурсию вместо итерации. Но наибольшую практическую пользу можно извлечь из обработки больших коллекций с помощью Stream API.
Писать программы в функциональном стиле на чистой java можно, но на практике это пока не оптимально. Для обработки потоков данных больше подходят библиотеки для реактивного программирования и стрим-фреймворки.// todo: refactor
Такие артефакты часто остаются в коде, иногда даже дополняются или переносятся в абстракции.
✅ Если Вы знаете, что пишете костыль — не оборачивайте его в абстракции, не приукрашивайте. Пусть он будет максимально понятным и грубым. Идеально — заведите отдельную задачу и сразу же пропишите, как исправить ситуацию.
Цель этого поста — не показать, что абстракций делать нельзя, а принципы проектирования нужны только для собеседований.
В больших системах over-engineering неизбежен. Однажды она станет тем самым душным легаси, в котором никто не хочет разбираться. В наших силах только замедлить этот процесс. Помните, что главная цель кода — решать бизнес-задачи. А паттерны, принципы и лучшие практики — это только инструменты для достижения этой цели.Object проверяет только равенство ссылок - по умолчанию каждый экземпляр класса равен только самому себе. Часто рекомендуют для новых классов переопределять метод equals и проверять равенство по значимым полям.
Несмотря на кажущуюся простоту, в реализации equals есть несколько тонких моментов. Кода в этом методе мало, выглядит он просто, а тесты на равенство редко кто пишет. Тем не менее часто встречается ситуация:
Странное поведение системы ➡️ неделя дебага по всей бизнес-логике ➡️ ошибка в equals ➡️ гнев и разочарование.
Простой способ не ошибиться — не переопределять equals вообще. Иногда это уместно:
1️⃣ Для перечислений (enum)
2️⃣ Когда важны сами экземпляры, а не данные внутри них. Пример - классы Thread, RecursiveTask.
3️⃣ Сравнение объектов вообще не предполагается. Пример - класс Pattern.
4️⃣ В базовом классе уже есть equals и нам подходит эта реализация.
5️⃣ Класс имеет модификатор доступа private или по умолчанию и мы уверены, что метод equals не будет вызван.
В остальных случаях его лучше переопределить. Во многих проектах при этом используется аннотация @EqualsAndHashCode библиотеки lombok, ограничения при её использовании мы тоже рассмотрим.
Итак, при реализации equals нужно учитывать следующие свойства:
1️⃣ Рефлексивность — экземпляр должен быть равен сам себе.
2️⃣ Консистентность — если экземпляры равны, то без внешних воздействий они должны оставаться равны.
✅ Сравнивать только неизменяемые поля.
Чтобы обезопасить себя от проблем с equals по возможности используйте для ключей в Map не классы целиком, а их неизменяемые поля, например, id.
Если используете аннотацию @EqualsAndHashCode явно указывайте поля, по которым идёт сравнение. Это поможет избежать ошибок при добавлении в класс изменяемого поля.
✅ @EqualsAndHashCode(of={“x”,“y”})
3️⃣ Симметрия.
Если в системе находятся объекты базового класса и его наследника нужно чётко понимать, как они будут между собой сравниваться.
Возьмём класс Точка, где равенство проверяется по координатам.
И его наследника — класс ЦветнаяТочка, где сравниваются координаты и цвет.
Точка обычная=new Точка(1,1);
Точка красная=new ЦветнаяТочка(1,1,RED);
Тогда обычная.equals(красная) будет true, а результат красная.equals(обычная) зависит от требований к системе — нужно ли игнорировать цвет в этом случае или нет.
4️⃣ Транзитивность.
Это свойство тоже легко нарушить, если мы имеем дело с иерархией:
Точка красная=new ЦветнаяТочка(1,1,RED);
Точка обычная=new Точка(1,1);
Точка синяя=new ЦветнаяТочка(1,1,BLUE);
Если мы решили игнорировать цвет при сравнении разных типов, то допустима такая ситуация:
красная.equals(обычная) - true
обычная.equals(синяя) — true
красная.equals(синяя) - true 😥
✅ Для иерархий классов писать юнит-тесты на симметричность и транзитивность.
✅ Не использовать @EqualsAndHashCode для иерархий классов.
🤔 Если в системе используются объекты базового и произвольного типов рассмотрите вариант замены наследования на композицию.
5️⃣ Сравнение существующего объекта с null должно возвращать false.
✅ Добавить отдельную проверку if(o == null)
Итого:
1️⃣ Не переопределяйте equals, если сравнение не предполагается.
2️⃣ Используйте в equals только неизменяемые поля.
3️⃣ Для иерархии классов пишите тесты на симметричность и транзитивность.
4️⃣ Для ключей в Map желательно использовать неизменяемые поля, например, ID.
5️⃣ Не использовать @EqualsAndHashCode для иерархии классов.
6️⃣ При использовании @EqualsAndHashCode явно прописывайте поля.
7️⃣ Кратко проверить два поля на null и равенство можно через Objects.equals(this.a, а)
8️⃣ Проверяйте первыми поля, которые вероятнее всего отличаются.
9️⃣ Не забывайте про hashcode — используйте те же поля, что в методе equals.Runnable action = () -> System.out.println("hey");
new Thread(action).start();
🅱️ расширить класс Thread, переопределить метод run, создать экземпляр этого класса:
class MyThread extends Thread {...}
new MyThread().start();
2️⃣ Углублённый ответ.
new Thread(...) приведёт к созданию в heap экземпляра класса Thread и сопутствующих объектов —program counter, хранилище thread-local переменных, нескольких стеков, объектов синхронизации и т.д. Вызов метода start() вызывает нативный метод start0(). Через JNI исполнится метод JVM JVM_StartThread, реализованный на C++, который вызывает метод ОС для создания потока. Для Unix это pthread_create.
3️⃣ Практический ответ.
Создание потока - ресурсоёмкая операция, поэтому лучше создать пул потоков, которые можно переиспользовать.
ExecutorService executor = Executors.newCachedThreadPool();
Runnable action = () -> System.out.println("hey");
executor.submit(action);
4️⃣ Неожиданный ответ.
Понятие потока в программировании очень размыто, особенно с учётом русского языка. Thread - структура для параллельного выполнения задач с помощью планировщика и потоков ОС.
Поток данных - тоже поток. В случае Stream API это скорее статичная коллекция, но можно рассказать интервьюеру основные методы его создания:
list.stream()
Stream.of(...)
Stream.range(...)
Arrays.stream(...)
Stream.generate(...)
5️⃣ Хайповый ответ.
Раз мы заговорили о потоке как потоке данных, то здесь можно свернуть в тему реактивного программирования. Самые популярные библиотеки для java это RxJava2, Project Reactor и Akka Streams. А также интерфейсы Flow API в java 9 и Reactive Streams.
Для примера рассмотрим создание потока в Reactor.
Поток из 0 или 1 элемента называется Mono:
Mono.just(1)
Mono.fromCallable(...)
Mono.fromFuture(...)
Mono.fromSupplier(...)
Поток из N элементов называется Flux:
Flux.just(1, 2, 3)
Flux.fromIterable(...)
Flux.fromStream(...)
Flux.range(...)
6️⃣ Big data oriented ответ.
В мире big data есть много фреймворков для обработки потоков данных. Apache Spark Streaming, Storm, Flink, Samza, Kafka Streams - если Вы работали с чем-то из этого списка, то обязательно поделитесь с интервьером этим опытом.
7️⃣ Менеджерский ответ.
Рассмотрим поток с нетехнической точки зрения. Поток - состояние, когда человек полностью включен в свою деятельность, совершенно сконцентрирован на ней и испытывает удовольствие от работы.
Задача руководителя - создать сотрудникам подходящие для этого условия. Можно использовать множество методов: регулярная обратная связь, увеличения прозрачности процессов и т.д.
Ответ на банальный вопрос "как создать поток" может различаться в зависимости от опыта и текущей должности. Вопросы на собеседованиях - лишь стартовая точка для обсуждений, не стесняйтесь повернуть разговор на обсуждение Ваших компетенций и сильных сторон.Xloggc:/path/to/gc.log
-XX:+PrintGCDetails
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime
Но вообще G1 адаптируется к нагрузке и прекрасно работает со значениями по умолчанию.
现已上线!2025 年 Telegram 研究 — 年度关键洞察 
