Как решить проблему долгой работы метода из-за бэсквинов?
Что проверяют
Умеете ли вы искать причину медленного метода, а не угадывать её. Слово в вопросе, скорее всего, искажено при расшифровке, поэтому сначала уточните, о чём речь: чаще всего имеют в виду повторяющиеся рекурсивные вызовы, реже лишние запросы к базе. Следом просят оценить сложность до и после исправления.
Как отвечать
Первый шаг это замер. Профиль снимают через Java Flight Recorder (-XX:StartFlightRecording) или async-profiler, микробенчмарк пишут на JMH, а не через System.currentTimeMillis() вокруг вызова: без прогрева JIT цифры случайны.
Если причина в рекурсии, где одни и те же подзадачи считаются заново, как в наивном Фибоначчи с его экспоненциальной сложностью, варианты такие:
- мемоизация: кэш результатов по аргументу, сложность падает до
O(n). Для одного потока хватитHashMapили массива; - итеративное решение снизу вверх: цикл с двумя переменными,
O(n)по времени иO(1)по памяти, без рискаStackOverflowError; - другой алгоритм: для Фибоначчи есть возведение матрицы в степень за
O(log n), на собеседовании достаточно показать, что вы о нём знаете.
Если же метод тормозит на базе, картина другая: классическая проблема N+1 в Hibernate, когда ленивая коллекция подгружается отдельным запросом на каждую сущность. Лечится JOIN FETCH, @EntityGraph или @BatchSize, а увидеть её помогает лог SQL.
Где спотыкаются
Мемоизируют через memo.computeIfAbsent(n, k -> fib(k - 1) + fib(k - 2)). Рекурсивная модификация той же HashMap внутри computeIfAbsent начиная с Java 9 бросает ConcurrentModificationException.
Рассчитывают, что JVM развернёт хвостовую рекурсию. Такой оптимизации в Java нет, глубокая рекурсия упирается в размер стека потока.
Берут long и не замечают переполнения: начиная с 93-го числа Фибоначчи значение в него не помещается, для больших номеров нужен BigInteger.