PrepBro
Профессии
PrepBro
Профессия:

Подготовка

  • Вопросы5784
  • Задачи58

Аналитика

  • hh статистика
  • Анализ резюме

Практика

  • Тестовое собеседование
  • Mock-собеседование
  • Менторы

Поддержка / отзывы

Telegram админа
Профессия:

Подготовка

  • Вопросы5784
  • Задачи58

Аналитика

  • hh статистика
  • Анализ резюме

Практика

  • Тестовое собеседование
  • Mock-собеседование
  • Менторы

Поддержка / отзывы

Telegram админа
Все 24 профессии
Android DeveloperData AnalystSystem Analyst1С DeveloperiOS DeveloperBusiness AnalystJava DeveloperData ScientistQA EngineerQA AutomationPHP BackendC/C++ BackendDevOps EngineerIT Project ManagerFrontend DeveloperNode.js BackendUnity DeveloperC# BackendProduct AnalystFlutter DeveloperPython DeveloperIT Product ManagerGo DeveloperData Engineer

© 2026 PrepBro. Все права защищены.

Telegram-бот

Задачи по Java Developer

Задачи с собеседований на Java-разработчика: Stream API и Collectors, многопоточность с AtomicInteger и BlockingQueue, воспроизведение deadlock, коллекции и ConcurrentModificationException, equals и hashCode, LRU-кеш, алгоритмы на строках и массивах. У каждой задачи есть разбор с рабочим кодом.

Подсчёт частоты слов с использованием Stream API
1.7 Middle🔥 25💬 1

Подсчёт частоты слов с Stream API

Это классическая задача на собеседовании, которая проверяет понимание Stream API, особенно groupingBy() коллектора.

Решение

import java.util.Map;
import java.util.stream.Collectors;
import java.util.Arrays;

public class WordFrequency {
    public static void main(String[] args) {
        String text = "java is great and java is fun";
        
        Map<String, Long> frequency = Arrays.stream(text.split(" "))
            .map(String::toLowerCase)
            .collect(Collectors.groupingBy(
                word -> word,
                Collectors.counting()
            ));
        
        System.out.println(frequency);
        // Вывод: {java=2, is=2, and=1, great=1, fun=1}
    }
}

Разбор решения пошагово

1. Разделение текста на слова

text.split(" ")  // Массив строк
// ["java", "is", "great", "and", "java", "is", "fun"]

2. Создание Stream из массива

Читать полностью ->
Найти ошибку: иерархия catch блоков
1.0 Junior🔥 25💬 1

Найти ошибку: иерархия catch блоков

Этот вопрос проверяет понимание иерархии исключений в Java и правил обработки исключений. Это очень распространенная ошибка, которую делают начинающие разработчики.

Анализ проблемы

1. Скомпилируется ли этот код?

Нет, этот код НЕ скомпилируется. Компилятор Java выдаст ошибку:

error: exception FileNotFoundException has already been caught

2. Почему это происходит?

Проблема в иерархии исключений. FileNotFoundException является подклассом IOException:

Throwable
  └─ Exception
       └─ IOException
            └─ FileNotFoundException

Когда catch блок ловит IOException, он ловит все исключения, которые являются IOException или его подклассами. Это включает и FileNotFoundException.

Поэтому второй catch блок для FileNotFoundException никогда не будет достигнут — он "недостижимый код" (unreachable code).

Визуализация проблемы

Читать полностью ->
ConcurrentModificationException при итерации ArrayList
1.2 Junior🔥 23💬 1

ConcurrentModificationException при итерации ArrayList

Ответ

Программа выбросит: ConcurrentModificationException

Почему возникает исключение?

Это происходит из-за механизма fail-fast итератора, встроенного в ArrayList. Когда вы изменяете размер списка во время итерации, итератор обнаруживает это и выбрасывает исключение.

Как это работает под капотом

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));

for (String s : list) {  // for-each использует итератор
    if (s.equals("b")) {
        list.add("d");   // ОШИБКА! Модифицируем список напрямую
    }
}
// java.util.ConcurrentModificationException

Внутреннее представление

// Примерно вот что происходит при for-each:

Iterator<String> iterator = list.iterator();
int modCount = list.modCount;  // Сохраняем начальное значение
Читать полностью ->
Two Sum - найти два числа с заданной суммой
1.3 Junior🔥 23💬 1

Two Sum: нахождение двух чисел с заданной суммой

Суть задачи

Это классическая задача на хеширование и поиск. Нужно найти два элемента массива, которые в сумме дают целевое число, и вернуть их индексы.

Подходы решения

1. Наивный подход — перебор (O(n²))

public int[] twoSum(int[] nums, int target) {
    for (int i = 0; i < nums.length; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[i] + nums[j] == target) {
                return new int[]{i, j};
            }
        }
    }
    return new int[]{};  // нет решения
}

Сложность: O(n²) — очень медленно при больших массивах.

2. Оптимальный подход — HashMap (O(n)) ✓

Идея: Проходим массив один раз. Для каждого числа проверяем, есть ли в HashMap число, которое в сумме с текущим даст target.

Читать полностью ->
FizzBuzz
1.0 Junior🔥 23💬 1

Подход

Итерируем от 1 до 100. Для каждого числа проверяем делимость: сначала на 15 (FizzBuzz), затем на 3 (Fizz), затем на 5 (Buzz), иначе выводим само число. Порядок проверки важен.

Решение

Читать полностью ->
Потокобезопасный счётчик с AtomicInteger
2.0 Middle🔥 20💬 1

Потокобезопасный счётчик с AtomicInteger

Проблема обычного int++ в многопоточной среде

Операция counter++ выглядит как одна команда, но на самом деле это три операции:

// counter++; эквивалентно:
int temp = counter;   // 1. Читаем значение (READ)
temp = temp + 1;      // 2. Увеличиваем (COMPUTE)
counter = temp;       // 3. Записываем (WRITE)

Проблема: race condition

Когда несколько потоков выполняют эти операции одновременно:

Поток 1: READ(100) -> COMPUTE(101) -> WRITE(101)
Поток 2: READ(100) -> COMPUTE(101) -> WRITE(101)
Поток 3: READ(100) -> COMPUTE(101) -> WRITE(101)

Желаемый результат: 103
Фактический результат: 101 (потеряли 2 инкремента!)

Это потому, что все потоки прочитали значение 100 перед тем, как кто-то его изменил.

Решение 1: synchronized

Читать полностью ->
Реализация простого REST API с Spring Boot
1.8 Middle🔥 20💬 1

Реализация простого REST API с Spring Boot

Это задача, которая проверяет владение Spring Boot и умение создавать производственный код с правильной обработкой ошибок, валидацией и правильными HTTP статусами.

Структура проекта

src/main/java/
├── com.example.todo/
│   ├── controller/
│   │   └── TodoController.java
│   ├── service/
│   │   └── TodoService.java
│   ├── repository/
│   │   └── TodoRepository.java
│   ├── entity/
│   │   └── Todo.java
│   ├── dto/
│   │   ├── CreateTodoRequest.java
│   │   ├── UpdateTodoRequest.java
│   │   └── TodoResponse.java
│   └── Application.java

1. Сущность Todo

package com.example.todo.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
Читать полностью ->
Определить цикл в LinkedList
1.0 Junior🔥 20💬 1

Определить цикл в LinkedList

Это классическая задача на определение цикла в связном списке. Её часто задают на собеседованиях, так как она требует понимания работы указателей и демонстрирует элегантное решение через алгоритм Флойда (Floyd's Cycle Detection).

Алгоритм Флойда: "Черепаха и заяц"

Идея очень простая: используем два указателя, которые движутся по списку с разной скоростью:

  • Черепаха (slow) — движется на 1 шаг за раз
  • Заяц (fast) — движется на 2 шага за раз

Если в списке есть цикл, эти два указателя в конце концов встретятся. Если список заканчивается (fast или fast.next становится null), цикла нет.

Структура ListNode

public class ListNode {
    int val;
    ListNode next;
    
    ListNode(int val) {
        this.val = val;
    }
}

Решение: определение наличия цикла

Читать полностью ->
Обмен значениями двух переменных без третьей переменной
1.0 Junior🔥 20💬 1

Подход

Используем арифметические операции для обмена значений без вспомогательной переменной. При сложении и вычитании сохраняем информацию о обеих значениях в одной переменной.

Решение

Читать полностью ->
Проверка на анаграмму
1.0 Junior🔥 19💬 1

Проверка на анаграмму в Java

Объяснение задачи

Анаграмма — это слово или фраза, составленные из тех же букв, что и исходное слово, но в другом порядке. Например, "listen" и "silent" содержат одинаковый набор букв: l, i, s, t, e, n.

Решение 1: С использованием массива (O(n))

Это оптимальное решение по времени. Подсчитываем частоту каждого символа:

public class AnagramChecker {
    public static boolean isAnagram(String str1, String str2) {
        if (str1.length() != str2.length()) {
            return false;
        }
        
        int[] charCount = new int[26];
        
        for (int i = 0; i < str1.length(); i++) {
            charCount[str1.charAt(i) - 'a']++;
            charCount[str2.charAt(i) - 'a']--;
        }
        
        for (int count : charCount) {
            if (count != 0) {
                return false;
            }
        }
        
        return true;
    }
}

Решение 2: С HashMap (более универсальное)

Читать полностью ->
Найти второе по величине число в массиве
1.0 Junior🔥 19💬 1

Подход

Одновременно отслеживаем первое и второе наибольшее число при проходе по массиву. За один проход обновляем обе переменные, учитывая повторяющиеся значения. Используем Integer.MIN_VALUE для инициализации.

Решение

Читать полностью ->
Фильтрация и сортировка списка объектов с Stream API
2.0 Middle🔥 18💬 1

Stream API: фильтрация, сортировка и сбор данных

Это классическая задача, демонстрирующая мощь Stream API в Java для функционального преобразования данных. Давайте разберёмся пошагово.

Полное решение в одну цепочку

List<String> topNames = employees.stream()
    .filter(emp -> emp.age > 30)           // шаг 1: фильтрация
    .sorted(Comparator.comparingDouble(Employee::getSalary).reversed())  // шаг 2: сортировка
    .limit(3)                               // шаг 3: топ-3
    .map(emp -> emp.name)                   // шаг 4: извлечение имён
    .collect(Collectors.toList());           // шаг 5: сбор в список

Полный рабочий пример

import java.util.*;
import java.util.stream.Collectors;
Читать полностью ->
Бинарный поиск
1.6 Junior🔥 18💬 1

Бинарный поиск

Суть алгоритма

Бинарный поиск — это быстрый алгоритм поиска элемента в отсортированном массиве за O(log n). Работает по принципу деления промежутка пополам.

Как работает

  1. Устанавливаем левую границу left = 0 и правую границу right = length - 1
  2. Находим средний элемент mid = (left + right) / 2
  3. Сравниваем элемент в позиции mid с target:
    • Если равны — найден!
    • Если nums[mid] < target — ищем в правой половине (left = mid + 1)
    • Если nums[mid] > target — ищем в левой половине (right = mid - 1)
  4. Повторяем, пока left <= right

Итеративная реализация

Читать полностью ->
Найти дублирующиеся символы в строке
1.0 Junior🔥 18💬 1

Подход

Проходим по строке, подсчитываем частоту каждого символа с помощью HashMap. Затем фильтруем результат, оставляя только символы с частотой больше 1. Выводим в порядке убывания частоты.

Решение

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
Читать полностью ->
Deadlock: воспроизведение и решение
1.7 Middle🔥 17💬 1

Deadlock: воспроизведение и решение

Deadlock — это ситуация, когда два или более потока ждут друг друга бесконечно, так как каждый удерживает ресурс, который нужен другому. Это один из самых коварных багов в многопоточных приложениях — он может не проявляться месяцами, а затем неожиданно заморозить систему.

1. Воспроизведение Deadlock

Классический пример с двумя потоками

Читать полностью ->
Producer-Consumer с BlockingQueue
2.0 Middle🔥 17💬 1

Producer-Consumer паттерн с BlockingQueue

Это один из самых важных паттернов для многопоточного программирования. BlockingQueue автоматически управляет синхронизацией между производителями и потребителями.

Основное решение

import java.util.concurrent.*;

public class ProducerConsumerExample {
    private static final int QUEUE_SIZE = 10;
    private static final int ITEMS_TO_PRODUCE = 100;
    private static final Integer POISON_PILL = -1; // Сигнал завершения

public static void main(String[] args) throws InterruptedException {
        // Ограниченная очередь размером 10
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(QUEUE_SIZE);

// ExecutorService для управления потоками
        ExecutorService executorService = Executors.newFixedThreadPool(3);

try {
            // Запуск 1 Producer
            executorService.submit(new Producer(queue, ITEMS_TO_PRODUCE));
Читать полностью ->
Подсчёт количества слов в строке с помощью HashMap
1.3 Junior🔥 17💬 1

Подход

Разделяем строку на слова, приводим их к нижнему регистру и подсчитываем частоту каждого слова с помощью HashMap. Используем метод getOrDefault() для элегантного обновления счётчика.

Решение

import java.util.HashMap;
import java.util.Map;
Читать полностью ->
Реализация LRU Cache
1.8 Middle🔥 16💬 1

Реализация LRU Cache

Объяснение задачи

LRU Cache (Least Recently Used) — это кэш, который автоматически удаляет наименее недавно используемый элемент при достижении максимальной ёмкости.

Основные операции:

  • get(key) — O(1) получить значение
  • put(key, value) — O(1) добавить/обновить
  • При переполнении удаляется самый старый неиспользованный элемент

Решение 1: LinkedHashMap (элегантное и простое)

LinkedHashMap поддерживает режим access-order:

import java.util.LinkedHashMap;
import java.util.Map;
Читать полностью ->
Найти i-й элемент с конца LinkedList за один проход
1.0 Junior🔥 16💬 1

Найти i-й элемент с конца LinkedList за один проход

Это классическая задача, которая проверяет понимание работы с указателями и оптимизацию алгоритмов. Ключ к решению — использование двух указателей с фиксированным расстоянием между ними.

Подход: Two Pointers (Два указателя)

Идея в том, что мы используем медленный и быстрый указатели с расстоянием в i элементов между ними.

Алгоритм:

  1. Создаем два указателя: первый и второй
  2. Перемещаем второй указатель на i позиций вперед
  3. Затем перемещаем оба указателя вместе, пока второй не достигнет конца
  4. Когда второй указатель на конце, первый указатель находится на позиции i от конца

Полное решение с обработкой ошибок

Читать полностью ->
Преобразование строки в число без parseInt
1.2 Junior🔥 15💬 1

Преобразование строки в целое число без parseInt()

Это классическая задача на собеседованиях, которая проверяет понимание работы с символами, обработку исключений и граничные случаи. Решение требует реализации алгоритма парсинга с нуля.

Пошаговый алгоритм

  1. Trim — удалить пробелы в начале и конце
  2. Проверка пустоты — если строка пуста, выбросить исключение
  3. Обработка знака — определить, положительное или отрицательное число
  4. Валидация символов — проверить, что все оставшиеся символы цифры
  5. Конвертация — перевести каждую цифру в число и накопить результат
  6. Проверка переполнения — убедиться, что результат не выходит за границы int

Основное решение

Читать полностью ->
Угол между стрелками часов
1.0 Junior🔥 15💬 1

Угол между стрелками часов

Это интересная геометрическая задача, которая часто встречается на собеседованиях. Ключ к решению — понимание того, как движутся часовая и минутная стрелки.

Основные принципы

Минутная стрелка:

  • Полный оборот: 360° за 60 минут
  • За одну минуту: 360° / 60 = 6° за минуту
  • Положение: minutes * 6

Часовая стрелка:

  • Полный оборот: 360° за 12 часов
  • За один час: 360° / 12 = 30° за час
  • За одну минуту: 30° / 60 = 0.5° за минуту
  • Положение: (hours % 12) * 30 + minutes * 0.5

Угол между стрелками:

  • Разница: |minuteAngle - hourAngle|
  • Если больше 180°, возьмём меньший угол: 360 - angle

Решение

Читать полностью ->
Произведение элементов массива кроме текущего
1.0 Junior🔥 15💬 1

Произведение элементов массива кроме текущего

Это классическая задача, требующая логического мышления и оптимизации. Запрет на деление усложняет задачу, но открывает возможность использования префиксного и суффиксного произведения.

Решение 1: Префиксное и суффиксное произведение — O(n) время, O(n) память

Основная идея: для каждого элемента вычислить произведение всех элементов слева и произведение всех элементов справа, затем перемножить их.

Читать полностью ->
Сравнение строк: == vs equals()
1.0 Junior🔥 15💬 1

Сравнение строк: == vs equals()

Это один из самых частых вопросов на собеседованиях Java-разработчиков, и ответ требует глубокого понимания того, как Java работает со строками. Давайте разберёмся с примерами.

Результаты выполнения кода

String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = new String("Hello");

System.out.println(s1 == s2);         // true ✅
System.out.println(s1 == s3);         // false ❌
System.out.println(s1.equals(s3));    // true ✅
System.out.println(s3 == s4);         // false ❌
System.out.println(s3.equals(s4));    // true ✅

Почему такие результаты?

Сравнение s1 == s2 → true

Обе переменные указывают на один и тот же объект в String Pool:

┌─────────────────────────┐
│    String Pool (Heap)   │
├─────────────────────────┤
│   "Hello" (объект)     │◄─── s1
└─────────────────────────┘       s2
Читать полностью ->
Удаление дубликатов из отсортированного массива на месте
1.0 Junior🔥 14💬 1

Удаление дубликатов из отсортированного массива на месте

Эта классическая задача на two-pointer техники демонстрирует, как эффективно работать с отсортированными данными. Ключевая идея — использовать два указателя: один для позиции записи, другой для сканирования массива.

Основная идея

Так как массив отсортирован, все дубликаты расположены рядом:

[0, 0, 1, 1, 1, 2, 2, 3, 3, 4]
 ↑     ↑        ↑     ↑  ↑  ↑
 дубликаты находятся рядом

Мы используем:

  • k — указатель на позицию для записи следующего уникального элемента
  • i — указатель для сканирования массива

Алгоритм:

  1. Начинаем с k=1, i=1 (первый элемент всегда уникален)
  2. Если nums[i] != nums[i-1] — нашли новый элемент
  3. Записываем nums[i] в позицию nums[k]
  4. Увеличиваем k и i
  5. Продолжаем пока не закончится массив

Решение

Читать полностью ->
Поворот массива на K позиций
1.2 Junior🔥 14💬 1

Поворот массива на K позиций

Объяснение задачи

Нужно повернуть массив вправо на k позиций на месте (in-place), без использования дополнительной памяти.

Пример: [1, 2, 3, 4, 5, 6, 7] при k=3 становится [5, 6, 7, 1, 2, 3, 4]

Решение 1: Метод трёх разворотов (самое оптимальное)

Идея: развернуть три части массива в определённом порядке:

Читать полностью ->
Поиск максимальной прибыли от акций
1.0 Junior🔥 14💬 1

Поиск максимальной прибыли от акций

Это одна из самых популярных задач на собеседованиях. Ее красота в простоте подхода и эффективности: одиночный проход по массиву дает оптимальное решение.

Решение 1: Однопроходный алгоритм — O(n) время, O(1) пространство

Основная идея: отслеживаем минимальную цену, встреченную до текущего момента, и вычисляем прибыль для текущей цены.

Читать полностью ->
Реализация структуры данных Stack
1.3 Junior🔥 14💬 1

Реализация структуры данных Stack

Стек — это одна из фундаментальных структур данных в компьютерной науке. Он используется везде: от обработки выражений и алгоритмов рекурсии до undo-функций в текстовых редакторах. Рассмотрим две основных реализации: на основе массива и на основе связного списка.

Stack на основе массива (ArrayList)

import java.util.ArrayList;
Читать полностью ->
Проверка строки на палиндром
1.0 Junior🔥 14💬 1

Подход

Преобразуем строку в нижний регистр, удаляем все не-буквенные символы и пробелы. Затем используем двойной указатель для проверки с обоих концов - если символы совпадают до середины, это палиндром.

Решение

Читать полностью ->
Проверка сбалансированности бинарного дерева
1.0 Junior🔥 13💬 1

Проверка сбалансированности бинарного дерева

Сбалансированное бинарное дерево (Balanced Binary Tree) — это дерево, в котором для каждого узла разница в глубинах между левым и правым поддеревьями не превышает 1.

Определение структуры узла

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    
    TreeNode(int val) {
        this.val = val;
    }
}

Основное решение (рекурсивное)

Читать полностью ->
Минимальный неблокирующий стек на AtomicReference
2.0 Middle🔥 13💬 1

Минимальный неблокирующий стек на AtomicReference

Эта задача требует создания lock-free структуры данных с использованием атомарных операций для безопасной работы в многопоточной среде без явных блокировок.

Основные концепции

Lock-free означает, что потоки не блокируют друг друга явно (synchronized, lock). Вместо этого используется optimistic locking с помощью Compare-And-Set (CAS) операций.

CAS операция:

  • Сравнивает текущее значение
  • Если оно совпадает с ожидаемым, устанавливает новое значение
  • Возвращает true/false в зависимости от результата

Реализация Lock-Free стека

import java.util.concurrent.atomic.AtomicReference;
Читать полностью ->
Поиск максимальной подстроки-палиндрома
1.6 Junior🔥 13💬 1

Поиск максимальной подстроки-палиндрома

Это классическая задача из категории строковых алгоритмов, которая требует рассмотрения нескольких подходов. Ключ к решению — понимание того, что палиндромы бывают нечетной и четной длины.

Решение 1: Расширение от центра — O(n²) время, O(1) пространство

Основная идея: для каждой возможной позиции центра разверните палиндром во все стороны.

Читать полностью ->
Функция: массив объектов → массив имён
1.3 Junior🔥 13💬 1

Извлечение имён из массива объектов

Суть задачи

Преобразовать массив объектов Person в массив строк (имён). Это базовая операция трансформации данных, которая в Java решается несколькими способами.

Определение класса Person

public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
}

Способ 1: Традиционный цикл for

Читать полностью ->
Числа Фибоначчи
1.2 Junior🔥 13💬 1

Подход

Реализуем три варианта: рекурсивный (базовый), рекурсивный с мемоизацией (оптимизированный) и итеративный (самый эффективный). Для рекурсии используем Map для кеширования результатов.

Решение

import java.util.HashMap;
import java.util.Map;
Читать полностью ->
Реализация equals() и hashCode()
1.2 Junior🔥 12💬 1

Реализация equals() и hashCode()

Это один из самых важных контрактов в Java — правильная реализация equals() и hashCode() критична для корректной работы с коллекциями, особенно с HashMap и HashSet.

Правильная реализация

import java.util.Objects;
Читать полностью ->
Потокобезопасный Singleton с double-checked locking
1.8 Middle🔥 12💬 1

Потокобезопасный Singleton с double-checked locking

1. Есть ли проблема в исходном коде?

ДА, ЕСТЬ СЕРЬЁЗНАЯ ПРОБЛЕМА! Код содержит классическую ошибку инициализации, связанную с переупорядочением инструкций компилятором и процессором.

Проблема в следующем:

При выполнении instance = new Singleton(); происходит несколько шагов:

  1. Выделение памяти для объекта
  2. Инициализация полей объекта (конструктор)
  3. Присваивание ссылки переменной instance

Однако из-за оптимизаций компилятора и CPU, шаги 2 и 3 могут быть переупорядочены. Это означает, что instance может быть установлена на объект ДО завершения его инициализации.

Сценарий критической ошибки:

Поток 1:
  1. Вошёл в synchronized блок
  2. Выделил память для объекта
  3. Установил instance (ОШИБКА: ещё не инициализирован!)
  4. Инициализирует объект
Читать полностью ->
Предсказать вывод: передача строки в метод
1.3 Junior🔥 12💬 1

Предсказать вывод: передача строки в метод

Ответ

Программа выведет: HelloHello

Почему именно HelloHello?

Это один из ключевых вопросов, тестирующих понимание:

  • Неизменяемости строк (String immutability)
  • Передачи параметров по значению (pass-by-value)
  • Ссылки на объекты в Java

Объяснение шаг за шагом

void foo() {
   String m = "Hello";              // m указывает на объект "Hello"
   System.out.print(m);             // Выводит: Hello
   bar(m);                          // Передаём ССЫЛКУ на m
   System.out.print(m);             // Выводит: Hello (не изменилось!)
}

void bar(String m) {
   m += " World!";                  // m ПЕРЕАССОЦИИРУЕТСЯ на новый объект
}

Ключевой момент: в методе bar() строка m переассоциируется на новый объект, созданный операцией конкатенации. Но это не влияет на переменную m в методе foo().

Внутреннее представление в памяти

Читать полностью ->
Найти дублированный элемент в массиве от 1 до 100
1.6 Junior🔥 12💬 1

Найти дублированный элемент в массиве

Объяснение задачи

Дан массив с числами от 1 до 100, где ровно один элемент повторяется один раз. Нужно найти это число за O(n) время и O(1) память.

Решение 1: Использование суммы (самое элегантное)

Идея: сумма чисел от 1 до n = n*(n+1)/2. Вычисляем разницу между фактической суммой и математической.

public class DuplicateFinder {
    /**
     * Находит дублированный элемент через сумму
     * Время: O(n), Память: O(1)
     */
    public static int findDuplicate(int[] arr) {
        int n = arr.length - 1; // Количество уникальных чисел
        
        // Математическая сумма от 1 до n
        long expectedSum = (long) n * (n + 1) / 2;
        
        // Фактическая сумма массива
        long actualSum = 0;
        for (int num : arr) {
            actualSum += num;
        }
        
        return (int) (actualSum - expectedSum);
    }
}
Читать полностью ->
Найти средний элемент LinkedList за один проход
1.0 Junior🔥 12💬 1

Поиск среднего элемента LinkedList за один проход

Суть задачи

Нужно найти средний элемент односвязного списка за один проход, не зная заранее длины списка. Классическое применение техники двух указателей (Two Pointers).

Техника двух указателей

Идея: Используем два указателя:

  • slow — движется на 1 шаг за раз
  • fast — движется на 2 шага за раз

Когда fast достигнет конца списка, slow будет находиться в середине.

Почему это работает

Список: 1 → 2 → 3 → 4 → 5 → null

Шаг 1: slow=1, fast=1
Шаг 2: slow=2, fast=3
Шаг 3: slow=3, fast=5
Шаг 4: slow=3, fast=null → fast достиг конца

Ответ: slow указывает на 3 (середину)

По математике: когда fast дойдет до конца, slow будет ровно в середине.

Реализация

class ListNode {
    int val;
    ListNode next;
    ListNode(int val) {
        this.val = val;
    }
}
Читать полностью ->
Сжатие строки (Run-Length Encoding)
1.2 Junior🔥 11💬 1

Сжатие строки методом Run-Length Encoding (RLE)

Run-Length Encoding — это простой, но эффективный алгоритм сжатия данных, который замещает последовательные одинаковые символы на сам символ и количество его повторений.

Алгоритм

  1. Проверить, будет ли сжатая строка меньше оригинала
  2. Пройти по строке и считать последовательные одинаковые символы
  3. Добавить в результат символ и его количество
  4. Если итоговая длина >= оригинальной, вернуть оригинал

Основное решение

Читать полностью ->
Капитализация первых букв слов в строке
1.3 Junior🔥 11💬 1

Капитализация первых букв слов в строке

Объяснение задачи

Нужно преобразовать каждое слово в строке так, чтобы первая буква была заглавной, а остальные — прописные. Например, "hello WORLD" → "Hello World".

Решение 1: StringBuilder (процедурный подход)

Это классический и эффективный способ:

Читать полностью ->
Реализация паттерна Singleton
1.3 Junior🔥 11💬 1

Реализация паттерна Singleton

Singleton — это один из самых известных и часто обсуждаемых паттернов проектирования. Он гарантирует, что класс имеет только один экземпляр в течение всего жизненного цикла приложения. Рассмотрим 5 различных способов реализации, от простейшего к самому продвинутому.

1. Eager Initialization (энергичная инициализация)

Экземпляр создаётся при загрузке класса:

public class SingletonEager {
    // Экземпляр создаётся сразу при загрузке класса
    private static final SingletonEager instance = new SingletonEager();
    
    // Приватный конструктор
    private SingletonEager() {
    }
    
    // Статический метод доступа
    public static SingletonEager getInstance() {
        return instance;
    }
}

Плюсы:

  • Простая реализация
  • Потокобезопасна по умолчанию (инициализация класса потокобезопасна в Java)
  • Нет проблем с отражением (Reflection)
Читать полностью ->
Развернуть LinkedList
1.3 Junior🔥 11💬 1

Развёртывание LinkedList

Суть задачи

Преобразовать порядок элементов в списке на противоположный: первый становится последним, второй — предпоследним и т.д. Это делается путём изменения направления указателей next.

Идея решения

У каждого узла есть указатель next. Нужно развернуть эти указатели в противоположном направлении:

Исходный список:  1 → 2 → 3 → null
Развёрнутый:      null ← 1 ← 2 ← 3

Итеративная реализация (O(1) память)

class ListNode {
    int val;
    ListNode next;
    ListNode(int val) {
        this.val = val;
    }
}
Читать полностью ->
Поиск дубликатов в массиве
1.0 Junior🔥 11💬 1

Поиск дубликатов в массиве

Это классическая задача на собеседованиях, проверяющая понимание структур данных и сложности алгоритмов. Рассмотрим несколько подходов с разной эффективностью.

Решение 1: HashSet (оптимальное) — O(n) время, O(n) память

Самое быстрое и популярное решение — использовать HashSet, который проверяет наличие элемента за O(1):

Читать полностью ->
Удаление пробелов из строки без replace()
1.3 Junior🔥 11💬 1

Подход

Проходим по строке символ за символом, добавляя в StringBuilder только те символы, которые не являются пробелами. Используем StringBuilder для эффективного конкатенирования.

Решение

Читать полностью ->
Группировка объектов по полю с Collectors
1.0 Junior🔥 10💬 1

Группировка объектов по полю с Collectors

Эта задача демонстрирует один из самых полезных паттернов в Stream API — группировку данных по ключам с агрегацией значений. Это идеален для обработки различных бизнес-сценариев: финансовые отчёты, аналитика, трансформация данных.

Решение

class Transaction {
    String type;    // "DEPOSIT", "WITHDRAWAL", "TRANSFER"
    double amount;
    
    public Transaction(String type, double amount) {
        this.type = type;
        this.amount = amount;
    }
    
    public String getType() {
        return type;
    }
    
    public double getAmount() {
        return amount;
    }
}
Читать полностью ->
Валидация скобочной последовательности
1.2 Junior🔥 10💬 1

Валидация скобочной последовательности

Это классическая задача на использование Stack (стека) — одной из фундаментальных структур данных в программировании. Идея решения очень простая: мы проходим по каждому символу и либо добавляем открывающую скобку в стек, либо проверяем, что закрывающая скобка соответствует последней открывающей.

Алгоритм

  1. Создаём пустой стек
  2. Для каждого символа в строке:
    • Если это открывающая скобка (, {, [ — добавляем её в стек
    • Если это закрывающая скобка, проверяем:
      • Стек не пуст
      • Скобка на вершине стека соответствует закрывающей
  3. В конце стек должен быть пуст

Реализация на Java

import java.util.Stack;
Читать полностью ->
Разворот строки без встроенных функций
1.0 Junior🔥 10💬 1

Подход

Будем проходить по строке с обоих концов навстречу друг другу, обменивая символы местами. Используем массив char для прямого манипулирования символами и двойной указатель.

Решение

Читать полностью ->
Поиск максимальной подстроки без повторяющихся символов
1.0 Junior🔥 9💬 1

Поиск максимальной подстроки без повторяющихся символов

Это классическая задача на применение техники Sliding Window (скользящее окно). Алгоритм позволяет эффективно найти требуемую подстроку за линейное время O(n).

Алгоритм: Sliding Window

Идея очень простая:

  1. Используем два указателя: left и right, которые образуют «окно»
  2. Расширяем окно, добавляя символы справа
  3. Если находим повтор, сужаем окно слева
  4. Отслеживаем максимальную длину
Строка: "abcabcbb"

Шаг 1: окно [a] → max = 1
Шаг 2: окно [ab] → max = 2
Шаг 3: окно [abc] → max = 3
Шаг 4: встречаем 'a' (повтор) → удаляем слева [bca]
Шаг 5: окно [bcab] → видим 'b' повторяется → [cab]
Шаг 6: окно [cabc] → видим 'c' повторяется → [abc]
...

Решение с HashSet

Читать полностью ->
Предсказать вывод: абстрактный класс с конструктором
1.0 Junior🔥 9💬 1

Предсказание вывода: абстрактный класс с конструктором

Результат выполнения

Программа выведет:

This is abstract class constructor
This is demo class constructor

Почему так?

Это классический пример иерархии конструкторов в наследовании. При создании объекта дочернего класса Java автоматически вызывает конструктор родительского класса.

Порядок вызова конструкторов

  1. Сначала конструктор родителя (OurAbstractClass) — выводит "This is abstract class constructor"
  2. Потом конструктор дочернего класса (OurDemoClass) — выводит "This is demo class constructor"

Это происходит, потому что компилятор Java автоматически добавляет вызов super() в конструктор дочернего класса:

class OurDemoClass extends OurAbstractClass {
   public OurDemoClass() {
      super();  // ← вызов конструктора родителя (компилятор добавляет автоматически)
      System.out.println("This is demo class constructor");
   }
}
Читать полностью ->
Сортировка массива методом пузырька
1.3 Junior🔥 9💬 1

Сортировка массива методом пузырька

Bubble Sort — один из самых простых, но наименее эффективных алгоритмов сортировки. Несмотря на его неэффективность для больших данных, он остаётся важным в обучении, так как демонстрирует базовые принципы алгоритмов сортировки.

Почему "пузырька"?

Алгоритм называется "пузырьковой сортировкой" потому, что большие элементы "всплывают" (bubble up) к концу массива подобно пузырькам воздуха, поднимающимся в воде. На каждом проходе наибольший элемент "поднимается" на свою финальную позицию.

Базовый алгоритм

Читать полностью ->