Java: fill the gaps
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
نمایش بیشتر📈 تحلیل کانال تلگرام Java: fill the gaps
کانال Java: fill the gaps (@java_fillthegaps) در بخش زبانی روسی بازیگری فعال است. در حال حاضر جامعه شامل 12 549 مشترک است و جایگاه 10 120 را در دسته فناوری و برنامهها و رتبه 52 841 را در منطقه روسيا دارد.
📊 شاخصهای مخاطب و پویایی
از زمان ایجاد در невідомо، پروژه رشد سریعی داشته و 12 549 مشترک جذب کرده است.
بر اساس آخرین دادهها در تاریخ 07 ژوئن, 2026، کانال فعالیت پایداری دارد. در ۳۰ روز گذشته تغییر اعضا برابر -46 و در ۲۴ ساعت گذشته برابر 0 بوده و همچنان دسترسی گستردهای حفظ شده است.
- وضعیت تأیید: تأیید نشده
- نرخ تعامل (ER): میانگین تعامل مخاطب 34.72% است و در ۲۴ ساعت نخست پس از انتشار، محتوا معمولاً N/A% واکنش نسبت به کل مشترکان کسب میکند.
- دسترسی پستها: هر پست به طور میانگین 0 بازدید دریافت میکند. در اولین روز معمولاً 0 بازدید جمعآوری میشود.
- واکنشها و تعامل: مخاطبان بهطور فعال حمایت میکنند؛ میانگین واکنش به هر پست 0 است.
- علایق موضوعی: محتوا بر موضوعات کلیدی مانند redis, hashmap, linkedhashmap, индекс, фича تمرکز دارد.
📝 توضیح و سیاست محتوایی
نویسنده این فضا را محل بیان دیدگاههای شخصی توصیف میکند:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
به لطف بهروزرسانیهای پرتکرار (آخرین داده در تاریخ 08 ژوئن, 2026)، کانال همواره بهروز و دارای دسترسی بالاست. تحلیلها نشان میدهد مخاطبان بهطور فعال با محتوا تعامل دارند و آن را به نقطه اثرگذاری مهم در دسته فناوری و برنامهها تبدیل کردهاند.
❌ for(int i=0;i<100;i++) ✅ for(int i=0;i<HLIMIT;i++)3️⃣ Перенести код в отдельный метод Выделяем нужные строки → правый щелчок мышки → Refactor → Extract Method... И наоборот 4️⃣ Убрать лишние методы и переменные, "уплотнить" код Правый щёлк → Refactor → Inline Method/Inline Variable
❌ for(int i=0; i<100; i++) ✅ for(int i=0; i<HLIMIT; i++)3️⃣ Перенести код в отдельный метод Выделяем нужные строки → правый щелчок мышки → Refactor → Extract Method... И наоборот 4️⃣ Убрать лишние методы и переменные, "уплотнить" код Правый щёлк→Refactor→Inline Method/Inline Variable
❌ for(int i=0; i<100; i++) ✅ for(int i=0; i<HLIMIT; i++)3️⃣ Перенести код в отдельный метод Выделяем нужные строки → правый щелчок мышки → Refactor → Extract Method... И наоборот 4️⃣ Убрать лишние методы и переменные, "уплотнить" код Правый щёлк→Refactor→Inline Method/Inline Variable
class FileLogger {…}
class Service {
FileLogger logger=new FileLogger();
}
Сделаем код чуть лучше:
1️⃣ Dependency injection
это когда компоненты создаются не внутри класса, а передаются в конструкторах или сеттерах. Перенесём инициализацию логгера в конструктор:
class Service {
FileLogger logger;
Service (FileLogger logger) {
this.logger= logger;
}
}
✅ Класс не занимается инициализацией логгера
2️⃣ Dependency invertion
Буква D в аббревиатуре SOLID, формулировка состоит из двух частей:
▫️Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
▫️Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции.
Суть: пусть сервис работает не с конкретным логгером, а с интерфейсом
interface Logger {…}
class FileLogger implements Logger {…}
class Service {
Logger logger=new FileLogger();
}
✅ В интерфейсе доступно меньше методов, поэтому его проще использовать
✅ Реализацию легко заменить
✅ Оба класса проще тестировать
Почему используется слово "абстракция"? Группу методов можно выделить в интерфейс, в абстрактный класс и даже в обычный класс. Но интерфейс самый подходящий вариант.
3️⃣ IoC - Inversion of Control
В маленьких программах жизнь начинается в методе main(). Программист создаёт объекты, вызывает методы, все шаги явно прописаны.
Inversion of Control - это когда ход выполнения программы задаёт фреймворк. Spring смотрит на классы и аннотации, а затем создаёт объекты, связывает их вместе и не даёт программе завершиться.
@Component class FileLogger {…}
@Component class Service {
@Autowired
FileLogger logger;
}
✅ Меньше скучного кода
✅ Низкая связность - код легко менять, тестировать и переиспользовать
Spring создаёт обёртки классов и работает через Dependency Injection. Можно и по-другому: через паттерн фабричный метод, стратегия или сервис локатор.
⚔️Историческая справка.
Сервис локатор иногда встречается в легаси проектах. Это когда компоненты создаются в классе ServiceLocator, а другие классы получают к ним доступ через статические методы.
class ServiceLocator {
private static Logger logger=…
public static Logger getLogger() {
return logger;
}
}
public class Service {
private Logger logger = ServiceLocator.getLogger();
}
Резюме:
🔸Dependency injection - класс не создаёт компоненты напрямую, они передаются через конструктор или сеттер
🔸Dependency invertion - класс работает с другими компонентами через интерфейс
🔸Inversion of Control - ход программы задаёт фреймворк. Соединять компоненты может Dependency injection, фабричный метод, стратегия или сервис локатор.
❗️Ответ на вопрос перед постом:
Это словоблудие относится к Dependency injection
#теорияcollect(toMap(Student::id, Student::name))
Если ключи повторяются, то вылетит IllegalStateException. Чтобы этого избежать, укажите третьим параметром, как объединять значения:
collect(toMap(Student::city, Student::name, (a,b) -> a+" "+b))["Москва":"Антон Аня Эдуард"] Похоже на группировку по городу, но это не она. В результате получается одно значение, а не группа. 🔸groupingBy Настоящую группировку делает коллектор groupingBy:
Map<String, List<Student>>=… collect(groupingBy (Student::city))По умолчанию сгруппированные элементы объединяются в список. Чтобы получить что-нибудь другое, передайте в метод другой коллектор:
groupingBy(Student::city, toSet())Здесь нам наконец пригодятся коллекторы из предыдущего поста, которые подставим во второй аргумент. ▫️Города и количество студентов:
groupingBy(Student::city, counting())▫️Города и имена студентов:
groupingBy(Student::city, mapping(Student::name, toSet()))Передаём в groupingBy функцию для ключей и коллектор mapping для значений. Он же принимает другой коллектор toSet, чтобы было понятно, куда складывать результат. ▫️ID студента и средняя оценка за экзамены: ... Для этой задачи группировка не подходит. У одного студента только одна средняя оценка, количество итоговых элементов = количеству исходных. Группировать нечего, поэтому используем toMap:
toMap(Student::id, s -> s.marks().stream().расчёт())Вместо расчёт() - километр кода. Подсчёт среднего недоступен в интерфейсе Stream, а удобных методов перевода List<Integer> в IntStream нет. 🔸partitioningBy Метод делит элементы на две группы по заданному условию. Результат - map с двумя ключами - true и false. ▫️Делим студентов на москвичей и жителей других городов:
Map<Boolean,Set<Student>>…
partitioningBy(s-> s.city().equals("Moscow"), toSet())
Вместо toSet можно подставить другой коллектор. Посчитаем количество студентов:
partitioningBy(s-> s.city().equals("Moscow"), counting())
Не очень понятно, зачем нужен partitioningBy. groupingBy даёт такой же результат, но на 2 символа короче:
groupingBy(s-> s.city().equals("Moscow"), counting())
❓Ответ на вопрос перед постом
Вы поручили джуниору посчитать среди студентов тех, кто не сдал ни одного экзамена. Вариант 3 предлагает применить к набору студентов метод Integer::longValue, что невозможно. Остальные варианты переводят каждого студента в число 1 и суммируют все значения. Ошибочный ответ - 3.
#core list.stream()
2️⃣ .filter(e -> e != 3)
3️⃣ .count();
Терминальная операция collect собирает элементы стрима в другую структуру данных. Все подробности передаются через аргумент:
collect(Collector collector)
Коллекторы - статические методы класса Collectors, которые возвращают аргумент для метода collect. Я буду опускать основной класс и вместо Collectors.counting() будут писать counting(). Чтобы было короче.
Чаще всего элементы стрима собирают в обычную коллекцию:
▪️toCollection, toList, toSet
▪️toUnmodifiableSet, toUnmodifiableList
Set res=students.stream() .filter(…).collect(toSet())Ещё одна группа - коллекторы, которые возвращают одно значение: ▫️counting ▫️averagingToInt / Long / Double ▫️joining ▫️maxBy, minBy ▫️reducing ▫️summingInt / Long / Double ▫️summarizingInt / Long / Double Интересны здесь только два метода: 🔸 joining Соединяет элементы в одну строку:
chars.stream().collect(joining("-"));
// ['a','b','c'] → a-b-c
🔸summarizingInt
Возвращает объект IntSummaryStatistics, который содержит минимум, максимум, среднее, количество элементов и их сумму.
Остальные методы сами по себе бесполезны, так как есть простые аналоги:
list.stream().collect(counting()) // аналог list.stream().count()Коллекторы mapping, flatMapping и filtering применяют функцию к элементам перед отправкой в другой коллектор.
Set<Long> ids = … collect(mapping(Student::id, toSet())Использовать их напрямую тоже смысла нет. Проще применить к элементам map, flatmap или filter, а потом собрать результаты:
map(Student::id).collect(toSet())❓Зачем нужны эти методы? Они нужны в коллекторах groupingBy и partitioningBy, про них подробно поговорим завтра. #core
synchronized (lock) {
return coll.remove(object);
}
Изменения последовательны, данные в безопасности и всегда актуальны.
Что делает дефолтный метод removeIf? Берёт итератор, проверяет каждый элемент на соответствие условию и удаляет, если нужно.
Переложим на методы SynchronizedCollection. Синхронизация по lock берётся, отпускается, берётся, отпускается, и так несколько раз. При большой нагрузке управление перехватит другой поток, и произойдёт коллизия. Дефолтный метод не выполнит гарантий, заданных классом.
Ошибку легко исправить - переопределить метод removeIf:
synchronized (lock) {
return coll.removeIf(filter);
}
Проблема в том, что такие ошибки сложно обнаружить. Default метод появился в марте 2014, а класс обновили в июле 2019. 5 лет пользователи SynchronizedCollection пользовались ненадёжным методом.
5 лет! Может новым методом никто не пользовался. Может поток данных через коллекцию был небольшим. Может никаких последствий не было. А может были, неизвестно.
Даже такая безобидная фича как "методы по умолчанию" привела к ошибке. Мы можем вынести из неё пару best practices:
▫️Если интерфейс используется только внутри системы, достаточно написать тесты для всех реализаций.
▫️Для общедоступных библиотек по возможности избегать методов по умолчанию.
#core
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
