Библиотека собеса по Java | вопросы с собеседований
Open in Telegram
Вопросы с собеседований по Java и ответы на них. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/08c603b6 Для обратной связи: @proglibrary_feeedback_bot
Show more6 485
Subscribers
+124 hours
+87 days
+1330 days
Posts Archive
❓ Как работает HashMap внутри? Что происходит при коллизии и при достижении load factor?
HashMap — массив бакетов (Node[] table). Ключ хешируется, хеш определяет индекс бакета: index = hash & (capacity - 1
Что происходит при put(key, value):
Считается hash(key) — не просто hashCode(), а с дополнительным перемешиванием старших битов ((h = key.hashCode()) ^ (h >>> 16)). Это снижает коллизии при маленьком размере таблицы.
Находится бакет по индексу. Если пустой — кладём первым. Если нет — коллизия.
🔹 Коллизия:
До Java 8 → связный список в бакете. Поиск O(n) в худшем случае.
С Java 8 → когда в бакете больше 8 элементов и capacity >= 64, список превращается в красно-чёрное дерево. Поиск становится O(log n). Обратно в список — при сжатии ниже 6 элементов.
// Упрощённо: Node в списке или TreeNode в дереве
static class Node<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
🔹 Load factor и resize:
По умолчанию capacity = 16, loadFactor = 0.75. Порог = capacity * loadFactor = 12.
Как только элементов стало больше 12 — начинается resize(): таблица удваивается до 32, все элементы перераспределяются по новым бакетам. Это O(n) операция.
Поэтому если заранее знаете размер — задавайте начальную ёмкость:
// Хотим 1000 элементов без resize:
// 1000 / 0.75 ≈ 1334, берём следующую степень двойки
Map<String, Integer> map = new HashMap<>(2048);
🐸 Библиотека собеса по Java
#core❓ Как работает HashMap внутри? Что происходит при коллизии и при достижении load factor?
HashMap — массив бакетов (Node[] table). Ключ хешируется, хеш определяет индекс бакета: index = hash & (capacity - 1
Что происходит при put(key, value):
Считается hash(key) — не просто hashCode(), а с дополнительным перемешиванием старших битов ((h = key.hashCode()) ^ (h >>> 16)). Это снижает коллизии при маленьком размере таблицы.
Находится бакет по индексу. Если пустой — кладём первым. Если нет — коллизия.
🔹 Коллизия:
До Java 8 → связный список в бакете. Поиск O(n) в худшем случае.
С Java 8 → когда в бакете больше 8 элементов и capacity >= 64, список превращается в красно-чёрное дерево. Поиск становится O(log n). Обратно в список — при сжатии ниже 6 элементов.
// Упрощённо: Node в списке или TreeNode в дереве
static class Node<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
🔹 Load factor и resize:
По умолчанию capacity = 16, loadFactor = 0.75. Порог = capacity * loadFactor = 12.
Как только элементов стало больше 12 — начинается resize(): таблица удваивается до 32, все элементы перераспределяются по новым бакетам. Это O(n) операция.
Поэтому если заранее знаете размер — задавайте начальную ёмкость:
// Хотим 1000 элементов без resize:
// 1000 / 0.75 ≈ 1334, берём следующую степень двойки
Map<String, Integer> map = new HashMap<>(2048);
🐸 Библиотека собеса по Java
#core✔️ Java-тест: кэш, который врёт
Метод возвращает устаревшие данные. Иногда и только на нескольких потоках. Воспроизвести локально — нереально 👇
📦 Задание — code review
Команда добавила простой in-memory кэш для тяжёлых вычислений. На одном потоке работает идеально. На проде с нагрузкой — иногда возвращает старый результат или null.
@Component
public class PricingCache {
private final Map<String, BigDecimal> cache = new HashMap<>();
private final PricingEngine pricingEngine;
public PricingCache(PricingEngine pricingEngine) {
this.pricingEngine = pricingEngine;
}
public BigDecimal getPrice(String productId) {
if (cache.containsKey(productId)) {
return cache.get(productId);
}
BigDecimal price = pricingEngine.calculate(productId);
cache.put(productId, price);
return price;
}
public void invalidate(String productId) {
cache.remove(productId);
}
public void invalidateAll() {
cache.clear();
}
}
▪️ Объясни
— В чём проблема в коде
— Как переписать getPrice верно
Ставьте → 🔥, если нравится формат. Если нет → 🌚
💬 Решения под спойлер. Сравним, какое будет лучше.
🐸 Библиотека собеса по Java
#practise❓ Чем отличается ReentrantLock от synchronized?
🔹 synchronized — встроенный монитор JVM. Прост, но ограничен:
— нельзя прервать ожидание
— нет tryLock
— нельзя узнать, занят ли монитор
🔹 ReentrantLock из java.util.concurrent.locks — явная блокировка с расширенными возможностями:
ReentrantLock lock = new ReentrantLock();
// Попытка без ожидания
if (lock.tryLock()) {
try {
// критическая секция
} finally {
lock.unlock(); // обязателен!
}
}
// С таймаутом
if (lock.tryLock(500, TimeUnit.MILLISECONDS)) { ... }
// С прерыванием
lock.lockInterruptibly();
🔹 Когда synchronized
Простые сценарии, нет нужды в доп. функциях, меньше кода и риска ошибок.
🔹 Когда ReentrantLock
Нужен tryLock, таймаут, прерывание, несколько Condition, или fairness.
🐸 Библиотека собеса по Java
#concurrency❓ Для чего нужен @Conditional в Spring?
@Conditional регистрирует бин только при выполнении определённого условия.
@Bean
@Conditional(WindowsCondition.class)
public Service windowsService() {
return new WindowsService();
}
class WindowsCondition implements Condition {
public boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
return System.getProperty("os.name")
.contains("Windows");
}
}
Spring Boot предоставляет готовые: @ConditionalOnProperty, @ConditionalOnClass, @ConditionalOnMissingBean.
🐸 Библиотека собеса по Java
#springОсталось всего 4 места на курс по ИИ-агентам. 30 апреля закрываем набор окончательно.
В ГС честно рассказали:
— Кому курс не подойдет;
— Какой хардкор в программе (LangGraph, AutoGen, CrewAI);
— Как мы даем токены, чтобы вы не тратили свои деньги.
🏃♀️ Записаться, пока есть места
❓ В чем разница между параллелизмом и многопоточностью?
Многопоточность — это способность программы выполнять несколько потоков , где каждый поток может выполняться независимо, но не обязательно одновременно. Потоки могут переключаться между собой, создавая иллюзию одновременности, однако они могут работать на одном ядре процессора.
Параллелизм, с другой стороны, — это выполнение нескольких задач одновременно, обычно на разных процессорах или ядрах. В контексте параллелизма, задачи действительно выполняются одновременно.
🐸 Библиотека собеса по Java
#concurrency
❓ Что такое иммутабельный объект?
Иммутабельный объект — это объект, состояние которого невозможно изменить после его создания. Иммутабельность способствует безопасности потоков и предотвращению ошибок, связанных с изменениями состояния объектов. Примером может служить класс String.
🐸 Библиотека собеса по Java
#core
🏃♀️ Как провести вечер вторника с пользой для карьеры?
Включайте кружок там личное приглашение от спикера. 👆
Уже завтра в прямом эфире, разбираем архитектуру контекста в мультиагентных системах.
🤫 Секретный лут:
промик на 5.000₽. Он достанется только тем, кто придет на прямой эфир.
👉 Регистрируйтесь на трансляцию
❓ Что такое WeakReference?
WeakReference — это слабая ссылка на объект, которая не препятствует его удалению сборщиком мусора.
Если на объект есть только weak references, GC может удалить его при нехватке памяти.
WeakReference<Data> weak = new WeakReference<>(data);
data = null;
// GC может удалить объект
Data retrieved = weak.get(); // может вернуть null
Используется в кешах (WeakHashMap), где элементы могут быть удалены при нехватке памяти, или для предотвращения memory leaks в listeners.
🐸 Библиотека собеса по Java
#jvm❓ Что такое Phantom Reference?
PhantomReference — самая слабая ссылка в Java. Отличается от WeakReference тем, что get() всегда возвращает null.
Используется для отслеживания момента, когда объект удалён из памяти. После удаления объекта PhantomReference помещается в ReferenceQueue.
В отличие от finalize(), это надёжный способ отследить удаление объекта. Используется редко, в основном во внутренних механизмах или для управления off-heap памятью.
🐸 Библиотека собеса по Java
#jvm
❓ Как работает G1 GC?
G1 делит heap на равные регионы (~2048 штук) вместо фиксированных Young/Old/Perm зон. Каждый регион динамически назначается как Eden, Survivor или Old.
Ключевые фазы
→ Initial Mark — STW, помечает GC roots
→ Concurrent Mark — параллельно с приложением строит граф достижимости
→ Remark — STW, дочищает concurrent-фазу (SATB-алгоритм)
→ Cleanup / Evacuation — копирует живые объекты из регионов с наибольшим мусором (отсюда "Garbage First")
🐸 Библиотека собеса по Java
#core
❓ Что такое Optional и когда его НЕ стоит использовать?
Optional<T> — контейнер, который явно сигнализирует об отсутствии значения вместо null.
Не стоит использовать
— В полях класса (не сериализуется хорошо)
— В параметрах методов (лучше перегрузка)
— В Collections (List<Optional<T>> — антипаттерн)
Стоит использовать: как возвращаемый тип, когда значение действительно может отсутствовать.
🐸 Библиотека собеса по Java
#core
❓ Что такое false sharing в многопоточности и как его избежать?
False sharing возникает, когда разные потоки обновляют разные переменные, находящиеся в одной кэш-линии CPU.
Несмотря на отсутствие логической связи, процессор вынужден постоянно инвалидировать кэш, создавая задержки.
Последствия: высокая латентность, падение пропускной способности, резкое ухудшение производительности в hot-paths.
Как избежать:
• Разнести горячие поля по разным кэш-линиям → использовать @Contended (JDK 8+, требует -XX:-RestrictContended).
• Заполнять структуру “паддингом” вручную.
• Перепроектировать алгоритм, чтобы снизить частоту записи.
🐸 Библиотека собеса по Java
#concurrency
❓ Чем BeanFactory отличается от ApplicationContext?
BeanFactory — минималистичный IoC-контейнер. Умеет создавать бины и резолвить зависимости. Бины создаются лениво — только при первом getBean(). Используется в очень ресурсоограниченных средах (embedded-системы), в обычных Spring-приложениях практически не встречается.
ApplicationContext расширяет BeanFactory и добавляет всё, без чего реальное приложение не живёт: eager-инициализацию singleton'ов при старте, поддержку событий (ApplicationEvent), интернационализацию (MessageSource), lifecycle-коллбэки (@PostConstruct / @PreDestroy), AOP-интеграцию и работу с окружением (Environment / @Value).
⚠️ ApplicationContext создаёт все singleton-бины сразу при старте. Это значит: ошибки конфигурации (неверные зависимости, битые @Value) падают немедленно, а не при первом обращении к бину в рантайме.
🐸 Библиотека собеса по Java
#spring
🏃♀️ Мы собрали бесплатный мега-гайд по ии-агентам 👇
В первой части постов навалили жесткой базы, чтобы вправить мозги на место. Во второй дали конкретные инструменты, фреймворки и пошаговые инструкции, что нужно кодить прямо сейчас.
Часть 1. Введение, юзкейсы и реальность
Разбираемся с терминами, снимаем розовые очки и смотрим, где ИИ реально приносит бабки, а где только жжет нервы:
1. «Так что вообще считается AI-агентом?»
2. «Где тут бот, а где уже AI-агент?»
3. «Не надо пихать AI-агента в каждую задачу»
4. «Что уже можно спокойно делать через AI-агентов?»
5. «А что через AI-агентов пока лучше не трогать?»
Часть 2. Изнанка, ошибки и архитектура
Как всё это устроено под капотом, чтобы не слить бюджет и не наломать дров на старте:
6. «Можно ли просто сесть вечером и собрать себе AI-агента?»
7. «С чего вообще начать, если хочется попробовать AI-агентов»
8. «Почему AI-агент может внезапно начать творить дичь»
9. «Где AI-агенты реально экономят время, а где только добавляют возни»
10. «Почему они жрут столько денег?»
Часть 3. Хардкорная практика (Что делать руками)
Хватит теории. Открываем ноут, запускаем Cursor и делаем нормальные, отказоустойчивые системы:
11. «Почему одного промпта мало?»
12. «Почему AI-агенту мало просто “дать доступ к данным”»
13. «Если не следить за AI-агентом, он быстро начинает жить своей жизнью»
14. «Собрать демку легко. Но как же сделать нормально»
15. «Как сделать, чтобы это не развалилось через неделю?»
👍 Сохраняйте пост в избранное, чтобы не потерять.
🤫 А завтра стартует наш курс по ии-агентам
💬 Обратная связь
Ежемесячный опрос по грейдам. Сколько среди нас джедаев мидлов?
Ваш грейд:
🔥 — Senior
👍🏼 — Middle
❤️ — Junior
😁 — Ещё учусь
🐸 Библиотека собеса по Java
✔️ Java-тест: бин-синглтон, который не синглтон
Работает у одного — сломано у другого. В логах — каша из чужих данных 👇
📦 Задание — code review
Команда добавила контекст текущего пользователя в сервис через поле. Локально — всё ок.
На проде с несколькими потоками — пользователи видят чужие данные.
@Service
public class ReportService {
private User currentUser;
private ReportFilter activeFilter;
private final ReportRepository reportRepository;
public ReportService(ReportRepository reportRepository) {
this.reportRepository = reportRepository;
}
public void initContext(User user, ReportFilter filter) {
this.currentUser = user;
this.activeFilter = filter;
}
public List<Report> getReports() {
if (currentUser == null) {
throw new IllegalStateException("Context not initialized");
}
return reportRepository.findByUserAndFilter(
currentUser.getId(),
activeFilter
);
}
public ReportSummary getSummary() {
List<Report> reports = getReports();
return ReportSummary.calculate(reports, currentUser);
}
}
▪️ Объясни
— Какой скоуп у бина по умолчанию в Spring и почему это делает поля-состояния опасными.
— Почему synchronized над методами не является правильным решением здесь.
— Как переписать код, чтобы работало.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
💬 Решения под спойлер. Сравним, какое будет лучше.
🐸 Библиотека собеса по Java
#practise❓ Можно ли изменить значение final переменной через рефлексию?
Технически можно, но это нарушает контракт final и приводит к непредсказуемому поведению.
Field field = obj.getClass().getDeclaredField("finalField");
field.setAccessible(true);
field.set(obj, newValue); // может не сработать
Проблемы
— JVM может заинлайнить final значение при компиляции
— изменения могут не отразиться из-за оптимизаций
— нарушается thread-safety гарантия final
🐸 Библиотека собеса по Java
#core
Available now! Telegram Research 2025 — the year's key insights 
