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، تحافظ القناة على نشاط مستقر. خلال آخر 30 يوماً تغيّر عدد الأعضاء بمقدار -46، وفي آخر 24 ساعة بمقدار 0، مع بقاء الوصول العام مرتفعاً.
- حالة التحقق: غير موثّقة
- معدل التفاعل (ER): يبلغ متوسط تفاعل الجمهور 34.72%. وخلال أول 24 ساعة من النشر يحصد المحتوى عادةً 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
متاح الآن! بحث تيليغرام 2025 — أهم رؤى العام 
