Java: fill the gaps
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
Mostrar más📈 Análisis del canal de Telegram Java: fill the gaps
El canal Java: fill the gaps (@java_fillthegaps) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 12 549 suscriptores, ocupando la posición 10 120 en la categoría Tecnologías y Aplicaciones y el puesto 52 841 en la región Rusia.
📊 Métricas de audiencia y dinámica
Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 12 549 suscriptores.
Según los últimos datos del 07 junio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -46, y en las últimas 24 horas de 0, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 34.72%. Durante las primeras 24 horas tras publicar, el contenido suele obtener N/A% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 0 visualizaciones. En el primer día suele acumular 0 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 0.
- Intereses temáticos: El contenido se centra en temas clave como redis, hashmap, linkedhashmap, индекс, фича.
📝 Descripción y política de contenido
El autor describe el recurso como un espacio para expresar opiniones subjetivas:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 08 junio, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.
❌ 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
¡Ya disponible! Investigación de Telegram 2025 — los principales insights del año 
